1 /*
2 * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package javax.swing;
27
28 import java.awt.*;
29 import java.awt.event.*;
30 import java.beans.*;
31 import java.io.*;
32 import java.util.*;
33 import javax.swing.event.*;
34 import javax.swing.plaf.*;
35 import javax.swing.tree.*;
36 import javax.swing.text.Position;
37 import javax.accessibility.*;
38 import sun.swing.SwingUtilities2;
39 import sun.swing.SwingUtilities2.Section;
40 import static sun.swing.SwingUtilities2.Section.*;
41
42
43 /**
44 * <a name="jtree_description">
45 * A control that displays a set of hierarchical data as an outline.
46 * You can find task-oriented documentation and examples of using trees in
47 * <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/tree.html">How to Use Trees</a>,
48 * a section in <em>The Java Tutorial.</em>
49 * <p>
50 * A specific node in a tree can be identified either by a
51 * <code>TreePath</code> (an object
52 * that encapsulates a node and all of its ancestors), or by its
53 * display row, where each row in the display area displays one node.
54 * An <i>expanded</i> node is a non-leaf node (as identified by
55 * <code>TreeModel.isLeaf(node)</code> returning false) that will displays
56 * its children when all its ancestors are <i>expanded</i>.
57 * A <i>collapsed</i>
58 * node is one which hides them. A <i>hidden</i> node is one which is
59 * under a collapsed ancestor. All of a <i>viewable</i> nodes parents
60 * are expanded, but may or may not be displayed. A <i>displayed</i> node
61 * is both viewable and in the display area, where it can be seen.
62 * <p>
63 * The following <code>JTree</code> methods use "visible" to mean "displayed":
64 * <ul>
65 * <li><code>isRootVisible()</code>
66 * <li><code>setRootVisible()</code>
67 * <li><code>scrollPathToVisible()</code>
68 * <li><code>scrollRowToVisible()</code>
69 * <li><code>getVisibleRowCount()</code>
70 * <li><code>setVisibleRowCount()</code>
71 * </ul>
72 * <p>
73 * The next group of <code>JTree</code> methods use "visible" to mean
74 * "viewable" (under an expanded parent):
75 * <ul>
76 * <li><code>isVisible()</code>
77 * <li><code>makeVisible()</code>
78 * </ul>
79 * <p>
80 * If you are interested in knowing when the selection changes implement
81 * the <code>TreeSelectionListener</code> interface and add the instance
82 * using the method <code>addTreeSelectionListener</code>.
83 * <code>valueChanged</code> will be invoked when the
84 * selection changes, that is if the user clicks twice on the same
85 * node <code>valueChanged</code> will only be invoked once.
86 * <p>
87 * If you are interested in detecting either double-click events or when
88 * a user clicks on a node, regardless of whether or not it was selected,
89 * we recommend you do the following:
90 * <pre>
91 * final JTree tree = ...;
92 *
93 * MouseListener ml = new MouseAdapter() {
94 * public void <b>mousePressed</b>(MouseEvent e) {
95 * int selRow = tree.getRowForLocation(e.getX(), e.getY());
96 * TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
97 * if(selRow != -1) {
98 * if(e.getClickCount() == 1) {
99 * mySingleClick(selRow, selPath);
100 * }
101 * else if(e.getClickCount() == 2) {
102 * myDoubleClick(selRow, selPath);
103 * }
104 * }
105 * }
106 * };
107 * tree.addMouseListener(ml);
108 * </pre>
109 * NOTE: This example obtains both the path and row, but you only need to
110 * get the one you're interested in.
111 * <p>
112 * To use <code>JTree</code> to display compound nodes
113 * (for example, nodes containing both
114 * a graphic icon and text), subclass {@link TreeCellRenderer} and use
115 * {@link #setCellRenderer} to tell the tree to use it. To edit such nodes,
116 * subclass {@link TreeCellEditor} and use {@link #setCellEditor}.
117 * <p>
118 * Like all <code>JComponent</code> classes, you can use {@link InputMap} and
119 * {@link ActionMap}
120 * to associate an {@link Action} object with a {@link KeyStroke}
121 * and execute the action under specified conditions.
122 * <p>
123 * <strong>Warning:</strong> Swing is not thread safe. For more
124 * information see <a
125 * href="package-summary.html#threading">Swing's Threading
126 * Policy</a>.
127 * <p>
128 * <strong>Warning:</strong>
129 * Serialized objects of this class will not be compatible with
130 * future Swing releases. The current serialization support is
131 * appropriate for short term storage or RMI between applications running
132 * the same version of Swing. As of 1.4, support for long term storage
133 * of all JavaBeans<sup><font size="-2">TM</font></sup>
134 * has been added to the <code>java.beans</code> package.
135 * Please see {@link java.beans.XMLEncoder}.
136 *
137 * @beaninfo
138 * attribute: isContainer false
139 * description: A component that displays a set of hierarchical data as an outline.
140 *
141 * @author Rob Davis
142 * @author Ray Ryan
143 * @author Scott Violet
144 */
145 public class JTree extends JComponent implements Scrollable, Accessible
146 {
147 /**
148 * @see #getUIClassID
149 * @see #readObject
150 */
151 private static final String uiClassID = "TreeUI";
152
153 /**
154 * The model that defines the tree displayed by this object.
155 */
156 transient protected TreeModel treeModel;
157
158 /**
159 * Models the set of selected nodes in this tree.
160 */
161 transient protected TreeSelectionModel selectionModel;
162
163 /**
164 * True if the root node is displayed, false if its children are
165 * the highest visible nodes.
166 */
167 protected boolean rootVisible;
168
169 /**
170 * The cell used to draw nodes. If <code>null</code>, the UI uses a default
171 * <code>cellRenderer</code>.
172 */
173 transient protected TreeCellRenderer cellRenderer;
174
175 /**
176 * Height to use for each display row. If this is <= 0 the renderer
177 * determines the height for each row.
178 */
179 protected int rowHeight;
180 private boolean rowHeightSet = false;
181
182 /**
183 * Maps from <code>TreePath</code> to <code>Boolean</code>
184 * indicating whether or not the
185 * particular path is expanded. This ONLY indicates whether a
186 * given path is expanded, and NOT if it is visible or not. That
187 * information must be determined by visiting all the parent
188 * paths and seeing if they are visible.
189 */
190 transient private Hashtable<TreePath, Boolean> expandedState;
191
192
193 /**
194 * True if handles are displayed at the topmost level of the tree.
195 * <p>
196 * A handle is a small icon that displays adjacent to the node which
197 * allows the user to click once to expand or collapse the node. A
198 * common interface shows a plus sign (+) for a node which can be
199 * expanded and a minus sign (-) for a node which can be collapsed.
200 * Handles are always shown for nodes below the topmost level.
201 * <p>
202 * If the <code>rootVisible</code> setting specifies that the root
203 * node is to be displayed, then that is the only node at the topmost
204 * level. If the root node is not displayed, then all of its
205 * children are at the topmost level of the tree. Handles are
206 * always displayed for nodes other than the topmost.
207 * <p>
208 * If the root node isn't visible, it is generally a good to make
209 * this value true. Otherwise, the tree looks exactly like a list,
210 * and users may not know that the "list entries" are actually
211 * tree nodes.
212 *
213 * @see #rootVisible
214 */
215 protected boolean showsRootHandles;
216 private boolean showsRootHandlesSet = false;
217
218 /**
219 * Creates a new event and passed it off the
220 * <code>selectionListeners</code>.
221 */
222 protected transient TreeSelectionRedirector selectionRedirector;
223
224 /**
225 * Editor for the entries. Default is <code>null</code>
226 * (tree is not editable).
227 */
228 transient protected TreeCellEditor cellEditor;
229
230 /**
231 * Is the tree editable? Default is false.
232 */
233 protected boolean editable;
234
235 /**
236 * Is this tree a large model? This is a code-optimization setting.
237 * A large model can be used when the cell height is the same for all
238 * nodes. The UI will then cache very little information and instead
239 * continually message the model. Without a large model the UI caches
240 * most of the information, resulting in fewer method calls to the model.
241 * <p>
242 * This value is only a suggestion to the UI. Not all UIs will
243 * take advantage of it. Default value is false.
244 */
245 protected boolean largeModel;
246
247 /**
248 * Number of rows to make visible at one time. This value is used for
249 * the <code>Scrollable</code> interface. It determines the preferred
250 * size of the display area.
251 */
252 protected int visibleRowCount;
253
254 /**
255 * If true, when editing is to be stopped by way of selection changing,
256 * data in tree changing or other means <code>stopCellEditing</code>
257 * is invoked, and changes are saved. If false,
258 * <code>cancelCellEditing</code> is invoked, and changes
259 * are discarded. Default is false.
260 */
261 protected boolean invokesStopCellEditing;
262
263 /**
264 * If true, when a node is expanded, as many of the descendants are
265 * scrolled to be visible.
266 */
267 protected boolean scrollsOnExpand;
268 private boolean scrollsOnExpandSet = false;
269
270 /**
271 * Number of mouse clicks before a node is expanded.
272 */
273 protected int toggleClickCount;
274
275 /**
276 * Updates the <code>expandedState</code>.
277 */
278 transient protected TreeModelListener treeModelListener;
279
280 /**
281 * Used when <code>setExpandedState</code> is invoked,
282 * will be a <code>Stack</code> of <code>Stack</code>s.
283 */
284 transient private Stack<Stack<TreePath>> expandedStack;
285
286 /**
287 * Lead selection path, may not be <code>null</code>.
288 */
289 private TreePath leadPath;
290
291 /**
292 * Anchor path.
293 */
294 private TreePath anchorPath;
295
296 /**
297 * True if paths in the selection should be expanded.
298 */
299 private boolean expandsSelectedPaths;
300
301 /**
302 * This is set to true for the life of the <code>setUI</code> call.
303 */
304 private boolean settingUI;
305
306 /** If true, mouse presses on selections initiate a drag operation. */
307 private boolean dragEnabled;
308
309 /**
310 * The drop mode for this component.
311 */
312 private DropMode dropMode = DropMode.USE_SELECTION;
313
314 /**
315 * The drop location.
316 */
317 private transient DropLocation dropLocation;
318
319 /**
320 * A subclass of <code>TransferHandler.DropLocation</code> representing
321 * a drop location for a <code>JTree</code>.
322 *
323 * @see #getDropLocation
324 * @since 1.6
325 */
326 public static final class DropLocation extends TransferHandler.DropLocation {
327 private final TreePath path;
328 private final int index;
329
330 private DropLocation(Point p, TreePath path, int index) {
331 super(p);
332 this.path = path;
333 this.index = index;
334 }
335
336 /**
337 * Returns the index where the dropped data should be inserted
338 * with respect to the path returned by <code>getPath()</code>.
339 * <p>
340 * For drop modes <code>DropMode.USE_SELECTION</code> and
341 * <code>DropMode.ON</code>, this index is unimportant (and it will
342 * always be <code>-1</code>) as the only interesting data is the
343 * path over which the drop operation occurred.
344 * <p>
345 * For drop mode <code>DropMode.INSERT</code>, this index
346 * indicates the index at which the data should be inserted into
347 * the parent path represented by <code>getPath()</code>.
348 * <code>-1</code> indicates that the drop occurred over the
349 * parent itself, and in most cases should be treated as inserting
350 * into either the beginning or the end of the parent's list of
351 * children.
352 * <p>
353 * For <code>DropMode.ON_OR_INSERT</code>, this value will be
354 * an insert index, as described above, or <code>-1</code> if
355 * the drop occurred over the path itself.
356 *
357 * @return the child index
358 * @see #getPath
359 */
360 public int getChildIndex() {
361 return index;
362 }
363
364 /**
365 * Returns the path where dropped data should be placed in the
366 * tree.
367 * <p>
368 * Interpretation of this value depends on the drop mode set on the
369 * component. If the drop mode is <code>DropMode.USE_SELECTION</code>
370 * or <code>DropMode.ON</code>, the return value is the path in the
371 * tree over which the data has been (or will be) dropped.
372 * <code>null</code> indicates that the drop is over empty space,
373 * not associated with a particular path.
374 * <p>
375 * If the drop mode is <code>DropMode.INSERT</code>, the return value
376 * refers to the path that should become the parent of the new data,
377 * in which case <code>getChildIndex()</code> indicates where the
378 * new item should be inserted into this parent path. A
379 * <code>null</code> path indicates that no parent path has been
380 * determined, which can happen for multiple reasons:
381 * <ul>
382 * <li>The tree has no model
383 * <li>There is no root in the tree
384 * <li>The root is collapsed
385 * <li>The root is a leaf node
386 * </ul>
387 * It is up to the developer to decide if and how they wish to handle
388 * the <code>null</code> case.
389 * <p>
390 * If the drop mode is <code>DropMode.ON_OR_INSERT</code>,
391 * <code>getChildIndex</code> can be used to determine whether the
392 * drop is on top of the path itself (<code>-1</code>) or the index
393 * at which it should be inserted into the path (values other than
394 * <code>-1</code>).
395 *
396 * @return the drop path
397 * @see #getChildIndex
398 */
399 public TreePath getPath() {
400 return path;
401 }
402
403 /**
404 * Returns a string representation of this drop location.
405 * This method is intended to be used for debugging purposes,
406 * and the content and format of the returned string may vary
407 * between implementations.
408 *
409 * @return a string representation of this drop location
410 */
411 public String toString() {
412 return getClass().getName()
413 + "[dropPoint=" + getDropPoint() + ","
414 + "path=" + path + ","
415 + "childIndex=" + index + "]";
416 }
417 }
418
419 /**
420 * The row to expand during DnD.
421 */
422 private int expandRow = -1;
423
424 private class TreeTimer extends Timer {
425 public TreeTimer() {
426 super(2000, null);
427 setRepeats(false);
428 }
429
430 public void fireActionPerformed(ActionEvent ae) {
431 JTree.this.expandRow(expandRow);
432 }
433 }
434
435 /**
436 * A timer to expand nodes during drop.
437 */
438 private TreeTimer dropTimer;
439
440 /**
441 * When <code>addTreeExpansionListener</code> is invoked,
442 * and <code>settingUI</code> is true, this ivar gets set to the passed in
443 * <code>Listener</code>. This listener is then notified first in
444 * <code>fireTreeCollapsed</code> and <code>fireTreeExpanded</code>.
445 * <p>This is an ugly workaround for a way to have the UI listener
446 * get notified before other listeners.
447 */
448 private transient TreeExpansionListener uiTreeExpansionListener;
449
450 /**
451 * Max number of stacks to keep around.
452 */
453 private static int TEMP_STACK_SIZE = 11;
454
455 //
456 // Bound property names
457 //
458 /** Bound property name for <code>cellRenderer</code>. */
459 public final static String CELL_RENDERER_PROPERTY = "cellRenderer";
460 /** Bound property name for <code>treeModel</code>. */
461 public final static String TREE_MODEL_PROPERTY = "model";
462 /** Bound property name for <code>rootVisible</code>. */
463 public final static String ROOT_VISIBLE_PROPERTY = "rootVisible";
464 /** Bound property name for <code>showsRootHandles</code>. */
465 public final static String SHOWS_ROOT_HANDLES_PROPERTY = "showsRootHandles";
466 /** Bound property name for <code>rowHeight</code>. */
467 public final static String ROW_HEIGHT_PROPERTY = "rowHeight";
468 /** Bound property name for <code>cellEditor</code>. */
469 public final static String CELL_EDITOR_PROPERTY = "cellEditor";
470 /** Bound property name for <code>editable</code>. */
471 public final static String EDITABLE_PROPERTY = "editable";
472 /** Bound property name for <code>largeModel</code>. */
473 public final static String LARGE_MODEL_PROPERTY = "largeModel";
474 /** Bound property name for selectionModel. */
475 public final static String SELECTION_MODEL_PROPERTY = "selectionModel";
476 /** Bound property name for <code>visibleRowCount</code>. */
477 public final static String VISIBLE_ROW_COUNT_PROPERTY = "visibleRowCount";
478 /** Bound property name for <code>messagesStopCellEditing</code>. */
479 public final static String INVOKES_STOP_CELL_EDITING_PROPERTY = "invokesStopCellEditing";
480 /** Bound property name for <code>scrollsOnExpand</code>. */
481 public final static String SCROLLS_ON_EXPAND_PROPERTY = "scrollsOnExpand";
482 /** Bound property name for <code>toggleClickCount</code>. */
483 public final static String TOGGLE_CLICK_COUNT_PROPERTY = "toggleClickCount";
484 /** Bound property name for <code>leadSelectionPath</code>.
485 * @since 1.3 */
486 public final static String LEAD_SELECTION_PATH_PROPERTY = "leadSelectionPath";
487 /** Bound property name for anchor selection path.
488 * @since 1.3 */
489 public final static String ANCHOR_SELECTION_PATH_PROPERTY = "anchorSelectionPath";
490 /** Bound property name for expands selected paths property
491 * @since 1.3 */
492 public final static String EXPANDS_SELECTED_PATHS_PROPERTY = "expandsSelectedPaths";
493
494
495 /**
496 * Creates and returns a sample <code>TreeModel</code>.
497 * Used primarily for beanbuilders to show something interesting.
498 *
499 * @return the default <code>TreeModel</code>
500 */
501 protected static TreeModel getDefaultTreeModel() {
502 DefaultMutableTreeNode root = new DefaultMutableTreeNode("JTree");
503 DefaultMutableTreeNode parent;
504
505 parent = new DefaultMutableTreeNode("colors");
506 root.add(parent);
507 parent.add(new DefaultMutableTreeNode("blue"));
508 parent.add(new DefaultMutableTreeNode("violet"));
509 parent.add(new DefaultMutableTreeNode("red"));
510 parent.add(new DefaultMutableTreeNode("yellow"));
511
512 parent = new DefaultMutableTreeNode("sports");
513 root.add(parent);
514 parent.add(new DefaultMutableTreeNode("basketball"));
515 parent.add(new DefaultMutableTreeNode("soccer"));
516 parent.add(new DefaultMutableTreeNode("football"));
517 parent.add(new DefaultMutableTreeNode("hockey"));
518
519 parent = new DefaultMutableTreeNode("food");
520 root.add(parent);
521 parent.add(new DefaultMutableTreeNode("hot dogs"));
522 parent.add(new DefaultMutableTreeNode("pizza"));
523 parent.add(new DefaultMutableTreeNode("ravioli"));
524 parent.add(new DefaultMutableTreeNode("bananas"));
525 return new DefaultTreeModel(root);
526 }
527
528 /**
529 * Returns a <code>TreeModel</code> wrapping the specified object.
530 * If the object is:<ul>
531 * <li>an array of <code>Object</code>s,
532 * <li>a <code>Hashtable</code>, or
533 * <li>a <code>Vector</code>
534 * </ul>then a new root node is created with each of the incoming
535 * objects as children. Otherwise, a new root is created with
536 * a value of {@code "root"}.
537 *
538 * @param value the <code>Object</code> used as the foundation for
539 * the <code>TreeModel</code>
540 * @return a <code>TreeModel</code> wrapping the specified object
541 */
542 protected static TreeModel createTreeModel(Object value) {
543 DefaultMutableTreeNode root;
544
545 if((value instanceof Object[]) || (value instanceof Hashtable) ||
546 (value instanceof Vector)) {
547 root = new DefaultMutableTreeNode("root");
548 DynamicUtilTreeNode.createChildren(root, value);
549 }
550 else {
551 root = new DynamicUtilTreeNode("root", value);
552 }
553 return new DefaultTreeModel(root, false);
554 }
555
556 /**
557 * Returns a <code>JTree</code> with a sample model.
558 * The default model used by the tree defines a leaf node as any node
559 * without children.
560 *
561 * @see DefaultTreeModel#asksAllowsChildren
562 */
563 public JTree() {
564 this(getDefaultTreeModel());
565 }
566
567 /**
568 * Returns a <code>JTree</code> with each element of the
569 * specified array as the
570 * child of a new root node which is not displayed.
571 * By default, the tree defines a leaf node as any node without
572 * children.
573 *
574 * @param value an array of <code>Object</code>s
575 * @see DefaultTreeModel#asksAllowsChildren
576 */
577 public JTree(Object[] value) {
578 this(createTreeModel(value));
579 this.setRootVisible(false);
580 this.setShowsRootHandles(true);
581 expandRoot();
582 }
583
584 /**
585 * Returns a <code>JTree</code> with each element of the specified
586 * <code>Vector</code> as the
587 * child of a new root node which is not displayed. By default, the
588 * tree defines a leaf node as any node without children.
589 *
590 * @param value a <code>Vector</code>
591 * @see DefaultTreeModel#asksAllowsChildren
592 */
593 public JTree(Vector<?> value) {
594 this(createTreeModel(value));
595 this.setRootVisible(false);
596 this.setShowsRootHandles(true);
597 expandRoot();
598 }
599
600 /**
601 * Returns a <code>JTree</code> created from a <code>Hashtable</code>
602 * which does not display with root.
603 * Each value-half of the key/value pairs in the <code>HashTable</code>
604 * becomes a child of the new root node. By default, the tree defines
605 * a leaf node as any node without children.
606 *
607 * @param value a <code>Hashtable</code>
608 * @see DefaultTreeModel#asksAllowsChildren
609 */
610 public JTree(Hashtable<?,?> value) {
611 this(createTreeModel(value));
612 this.setRootVisible(false);
613 this.setShowsRootHandles(true);
614 expandRoot();
615 }
616
617 /**
618 * Returns a <code>JTree</code> with the specified
619 * <code>TreeNode</code> as its root,
620 * which displays the root node.
621 * By default, the tree defines a leaf node as any node without children.
622 *
623 * @param root a <code>TreeNode</code> object
624 * @see DefaultTreeModel#asksAllowsChildren
625 */
626 public JTree(TreeNode root) {
627 this(root, false);
628 }
629
630 /**
631 * Returns a <code>JTree</code> with the specified <code>TreeNode</code>
632 * as its root, which
633 * displays the root node and which decides whether a node is a
634 * leaf node in the specified manner.
635 *
636 * @param root a <code>TreeNode</code> object
637 * @param asksAllowsChildren if false, any node without children is a
638 * leaf node; if true, only nodes that do not allow
639 * children are leaf nodes
640 * @see DefaultTreeModel#asksAllowsChildren
641 */
642 public JTree(TreeNode root, boolean asksAllowsChildren) {
643 this(new DefaultTreeModel(root, asksAllowsChildren));
644 }
645
646 /**
647 * Returns an instance of <code>JTree</code> which displays the root node
648 * -- the tree is created using the specified data model.
649 *
650 * @param newModel the <code>TreeModel</code> to use as the data model
651 */
652 @ConstructorProperties({"model"})
653 public JTree(TreeModel newModel) {
654 super();
655 expandedStack = new Stack<Stack<TreePath>>();
656 toggleClickCount = 2;
657 expandedState = new Hashtable<TreePath, Boolean>();
658 setLayout(null);
659 rowHeight = 16;
660 visibleRowCount = 20;
661 rootVisible = true;
662 selectionModel = new DefaultTreeSelectionModel();
663 cellRenderer = null;
664 scrollsOnExpand = true;
665 setOpaque(true);
666 expandsSelectedPaths = true;
667 updateUI();
668 setModel(newModel);
669 }
670
671 /**
672 * Returns the L&F object that renders this component.
673 *
674 * @return the <code>TreeUI</code> object that renders this component
675 */
676 public TreeUI getUI() {
677 return (TreeUI)ui;
678 }
679
680 /**
681 * Sets the L&F object that renders this component.
682 * <p>
683 * This is a bound property.
684 *
685 * @param ui the <code>TreeUI</code> L&F object
686 * @see UIDefaults#getUI
687 * @beaninfo
688 * bound: true
689 * hidden: true
690 * attribute: visualUpdate true
691 * description: The UI object that implements the Component's LookAndFeel.
692 */
693 public void setUI(TreeUI ui) {
694 if (this.ui != ui) {
695 settingUI = true;
696 uiTreeExpansionListener = null;
697 try {
698 super.setUI(ui);
699 }
700 finally {
701 settingUI = false;
702 }
703 }
704 }
705
706 /**
707 * Notification from the <code>UIManager</code> that the L&F has changed.
708 * Replaces the current UI object with the latest version from the
709 * <code>UIManager</code>.
710 *
711 * @see JComponent#updateUI
712 */
713 public void updateUI() {
714 setUI((TreeUI)UIManager.getUI(this));
715
716 SwingUtilities.updateRendererOrEditorUI(getCellRenderer());
717 SwingUtilities.updateRendererOrEditorUI(getCellEditor());
718 }
719
720
721 /**
722 * Returns the name of the L&F class that renders this component.
723 *
724 * @return the string "TreeUI"
725 * @see JComponent#getUIClassID
726 * @see UIDefaults#getUI
727 */
728 public String getUIClassID() {
729 return uiClassID;
730 }
731
732
733 /**
734 * Returns the current <code>TreeCellRenderer</code>
735 * that is rendering each cell.
736 *
737 * @return the <code>TreeCellRenderer</code> that is rendering each cell
738 */
739 public TreeCellRenderer getCellRenderer() {
740 return cellRenderer;
741 }
742
743 /**
744 * Sets the <code>TreeCellRenderer</code> that will be used to
745 * draw each cell.
746 * <p>
747 * This is a bound property.
748 *
749 * @param x the <code>TreeCellRenderer</code> that is to render each cell
750 * @beaninfo
751 * bound: true
752 * description: The TreeCellRenderer that will be used to draw
753 * each cell.
754 */
755 public void setCellRenderer(TreeCellRenderer x) {
756 TreeCellRenderer oldValue = cellRenderer;
757
758 cellRenderer = x;
759 firePropertyChange(CELL_RENDERER_PROPERTY, oldValue, cellRenderer);
760 invalidate();
761 }
762
763 /**
764 * Determines whether the tree is editable. Fires a property
765 * change event if the new setting is different from the existing
766 * setting.
767 * <p>
768 * This is a bound property.
769 *
770 * @param flag a boolean value, true if the tree is editable
771 * @beaninfo
772 * bound: true
773 * description: Whether the tree is editable.
774 */
775 public void setEditable(boolean flag) {
776 boolean oldValue = this.editable;
777
778 this.editable = flag;
779 firePropertyChange(EDITABLE_PROPERTY, oldValue, flag);
780 if (accessibleContext != null) {
781 accessibleContext.firePropertyChange(
782 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
783 (oldValue ? AccessibleState.EDITABLE : null),
784 (flag ? AccessibleState.EDITABLE : null));
785 }
786 }
787
788 /**
789 * Returns true if the tree is editable.
790 *
791 * @return true if the tree is editable
792 */
793 public boolean isEditable() {
794 return editable;
795 }
796
797 /**
798 * Sets the cell editor. A <code>null</code> value implies that the
799 * tree cannot be edited. If this represents a change in the
800 * <code>cellEditor</code>, the <code>propertyChange</code>
801 * method is invoked on all listeners.
802 * <p>
803 * This is a bound property.
804 *
805 * @param cellEditor the <code>TreeCellEditor</code> to use
806 * @beaninfo
807 * bound: true
808 * description: The cell editor. A null value implies the tree
809 * cannot be edited.
810 */
811 public void setCellEditor(TreeCellEditor cellEditor) {
812 TreeCellEditor oldEditor = this.cellEditor;
813
814 this.cellEditor = cellEditor;
815 firePropertyChange(CELL_EDITOR_PROPERTY, oldEditor, cellEditor);
816 invalidate();
817 }
818
819 /**
820 * Returns the editor used to edit entries in the tree.
821 *
822 * @return the <code>TreeCellEditor</code> in use,
823 * or <code>null</code> if the tree cannot be edited
824 */
825 public TreeCellEditor getCellEditor() {
826 return cellEditor;
827 }
828
829 /**
830 * Returns the <code>TreeModel</code> that is providing the data.
831 *
832 * @return the <code>TreeModel</code> that is providing the data
833 */
834 public TreeModel getModel() {
835 return treeModel;
836 }
837
838 /**
839 * Sets the <code>TreeModel</code> that will provide the data.
840 * <p>
841 * This is a bound property.
842 *
843 * @param newModel the <code>TreeModel</code> that is to provide the data
844 * @beaninfo
845 * bound: true
846 * description: The TreeModel that will provide the data.
847 */
848 public void setModel(TreeModel newModel) {
849 clearSelection();
850
851 TreeModel oldModel = treeModel;
852
853 if(treeModel != null && treeModelListener != null)
854 treeModel.removeTreeModelListener(treeModelListener);
855
856 if (accessibleContext != null) {
857 if (treeModel != null) {
858 treeModel.removeTreeModelListener((TreeModelListener)accessibleContext);
859 }
860 if (newModel != null) {
861 newModel.addTreeModelListener((TreeModelListener)accessibleContext);
862 }
863 }
864
865 treeModel = newModel;
866 clearToggledPaths();
867 if(treeModel != null) {
868 if(treeModelListener == null)
869 treeModelListener = createTreeModelListener();
870 if(treeModelListener != null)
871 treeModel.addTreeModelListener(treeModelListener);
872 // Mark the root as expanded, if it isn't a leaf.
873 if(treeModel.getRoot() != null &&
874 !treeModel.isLeaf(treeModel.getRoot())) {
875 expandedState.put(new TreePath(treeModel.getRoot()),
876 Boolean.TRUE);
877 }
878 }
879 firePropertyChange(TREE_MODEL_PROPERTY, oldModel, treeModel);
880 invalidate();
881 }
882
883 /**
884 * Returns true if the root node of the tree is displayed.
885 *
886 * @return true if the root node of the tree is displayed
887 * @see #rootVisible
888 */
889 public boolean isRootVisible() {
890 return rootVisible;
891 }
892
893 /**
894 * Determines whether or not the root node from
895 * the <code>TreeModel</code> is visible.
896 * <p>
897 * This is a bound property.
898 *
899 * @param rootVisible true if the root node of the tree is to be displayed
900 * @see #rootVisible
901 * @beaninfo
902 * bound: true
903 * description: Whether or not the root node
904 * from the TreeModel is visible.
905 */
906 public void setRootVisible(boolean rootVisible) {
907 boolean oldValue = this.rootVisible;
908
909 this.rootVisible = rootVisible;
910 firePropertyChange(ROOT_VISIBLE_PROPERTY, oldValue, this.rootVisible);
911 if (accessibleContext != null) {
912 ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
913 }
914 }
915
916 /**
917 * Sets the value of the <code>showsRootHandles</code> property,
918 * which specifies whether the node handles should be displayed.
919 * The default value of this property depends on the constructor
920 * used to create the <code>JTree</code>.
921 * Some look and feels might not support handles;
922 * they will ignore this property.
923 * <p>
924 * This is a bound property.
925 *
926 * @param newValue <code>true</code> if root handles should be displayed;
927 * otherwise, <code>false</code>
928 * @see #showsRootHandles
929 * @see #getShowsRootHandles
930 * @beaninfo
931 * bound: true
932 * description: Whether the node handles are to be
933 * displayed.
934 */
935 public void setShowsRootHandles(boolean newValue) {
936 boolean oldValue = showsRootHandles;
937 TreeModel model = getModel();
938
939 showsRootHandles = newValue;
940 showsRootHandlesSet = true;
941 firePropertyChange(SHOWS_ROOT_HANDLES_PROPERTY, oldValue,
942 showsRootHandles);
943 if (accessibleContext != null) {
944 ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
945 }
946 invalidate();
947 }
948
949 /**
950 * Returns the value of the <code>showsRootHandles</code> property.
951 *
952 * @return the value of the <code>showsRootHandles</code> property
953 * @see #showsRootHandles
954 */
955 public boolean getShowsRootHandles()
956 {
957 return showsRootHandles;
958 }
959
960 /**
961 * Sets the height of each cell, in pixels. If the specified value
962 * is less than or equal to zero the current cell renderer is
963 * queried for each row's height.
964 * <p>
965 * This is a bound property.
966 *
967 * @param rowHeight the height of each cell, in pixels
968 * @beaninfo
969 * bound: true
970 * description: The height of each cell.
971 */
972 public void setRowHeight(int rowHeight)
973 {
974 int oldValue = this.rowHeight;
975
976 this.rowHeight = rowHeight;
977 rowHeightSet = true;
978 firePropertyChange(ROW_HEIGHT_PROPERTY, oldValue, this.rowHeight);
979 invalidate();
980 }
981
982 /**
983 * Returns the height of each row. If the returned value is less than
984 * or equal to 0 the height for each row is determined by the
985 * renderer.
986 *
987 */
988 public int getRowHeight()
989 {
990 return rowHeight;
991 }
992
993 /**
994 * Returns true if the height of each display row is a fixed size.
995 *
996 * @return true if the height of each row is a fixed size
997 */
998 public boolean isFixedRowHeight()
999 {
1000 return (rowHeight > 0);
1001 }
1002
1003 /**
1004 * Specifies whether the UI should use a large model.
1005 * (Not all UIs will implement this.) Fires a property change
1006 * for the LARGE_MODEL_PROPERTY.
1007 * <p>
1008 * This is a bound property.
1009 *
1010 * @param newValue true to suggest a large model to the UI
1011 * @see #largeModel
1012 * @beaninfo
1013 * bound: true
1014 * description: Whether the UI should use a
1015 * large model.
1016 */
1017 public void setLargeModel(boolean newValue) {
1018 boolean oldValue = largeModel;
1019
1020 largeModel = newValue;
1021 firePropertyChange(LARGE_MODEL_PROPERTY, oldValue, newValue);
1022 }
1023
1024 /**
1025 * Returns true if the tree is configured for a large model.
1026 *
1027 * @return true if a large model is suggested
1028 * @see #largeModel
1029 */
1030 public boolean isLargeModel() {
1031 return largeModel;
1032 }
1033
1034 /**
1035 * Determines what happens when editing is interrupted by selecting
1036 * another node in the tree, a change in the tree's data, or by some
1037 * other means. Setting this property to <code>true</code> causes the
1038 * changes to be automatically saved when editing is interrupted.
1039 * <p>
1040 * Fires a property change for the INVOKES_STOP_CELL_EDITING_PROPERTY.
1041 *
1042 * @param newValue true means that <code>stopCellEditing</code> is invoked
1043 * when editing is interrupted, and data is saved; false means that
1044 * <code>cancelCellEditing</code> is invoked, and changes are lost
1045 * @beaninfo
1046 * bound: true
1047 * description: Determines what happens when editing is interrupted,
1048 * selecting another node in the tree, a change in the
1049 * tree's data, or some other means.
1050 */
1051 public void setInvokesStopCellEditing(boolean newValue) {
1052 boolean oldValue = invokesStopCellEditing;
1053
1054 invokesStopCellEditing = newValue;
1055 firePropertyChange(INVOKES_STOP_CELL_EDITING_PROPERTY, oldValue,
1056 newValue);
1057 }
1058
1059 /**
1060 * Returns the indicator that tells what happens when editing is
1061 * interrupted.
1062 *
1063 * @return the indicator that tells what happens when editing is
1064 * interrupted
1065 * @see #setInvokesStopCellEditing
1066 */
1067 public boolean getInvokesStopCellEditing() {
1068 return invokesStopCellEditing;
1069 }
1070
1071 /**
1072 * Sets the <code>scrollsOnExpand</code> property,
1073 * which determines whether the
1074 * tree might scroll to show previously hidden children.
1075 * If this property is <code>true</code> (the default),
1076 * when a node expands
1077 * the tree can use scrolling to make
1078 * the maximum possible number of the node's descendants visible.
1079 * In some look and feels, trees might not need to scroll when expanded;
1080 * those look and feels will ignore this property.
1081 * <p>
1082 * This is a bound property.
1083 *
1084 * @param newValue <code>false</code> to disable scrolling on expansion;
1085 * <code>true</code> to enable it
1086 * @see #getScrollsOnExpand
1087 *
1088 * @beaninfo
1089 * bound: true
1090 * description: Indicates if a node descendant should be scrolled when expanded.
1091 */
1092 public void setScrollsOnExpand(boolean newValue) {
1093 boolean oldValue = scrollsOnExpand;
1094
1095 scrollsOnExpand = newValue;
1096 scrollsOnExpandSet = true;
1097 firePropertyChange(SCROLLS_ON_EXPAND_PROPERTY, oldValue,
1098 newValue);
1099 }
1100
1101 /**
1102 * Returns the value of the <code>scrollsOnExpand</code> property.
1103 *
1104 * @return the value of the <code>scrollsOnExpand</code> property
1105 */
1106 public boolean getScrollsOnExpand() {
1107 return scrollsOnExpand;
1108 }
1109
1110 /**
1111 * Sets the number of mouse clicks before a node will expand or close.
1112 * The default is two.
1113 * <p>
1114 * This is a bound property.
1115 *
1116 * @since 1.3
1117 * @beaninfo
1118 * bound: true
1119 * description: Number of clicks before a node will expand/collapse.
1120 */
1121 public void setToggleClickCount(int clickCount) {
1122 int oldCount = toggleClickCount;
1123
1124 toggleClickCount = clickCount;
1125 firePropertyChange(TOGGLE_CLICK_COUNT_PROPERTY, oldCount,
1126 clickCount);
1127 }
1128
1129 /**
1130 * Returns the number of mouse clicks needed to expand or close a node.
1131 *
1132 * @return number of mouse clicks before node is expanded
1133 * @since 1.3
1134 */
1135 public int getToggleClickCount() {
1136 return toggleClickCount;
1137 }
1138
1139 /**
1140 * Configures the <code>expandsSelectedPaths</code> property. If
1141 * true, any time the selection is changed, either via the
1142 * <code>TreeSelectionModel</code>, or the cover methods provided by
1143 * <code>JTree</code>, the <code>TreePath</code>s parents will be
1144 * expanded to make them visible (visible meaning the parent path is
1145 * expanded, not necessarily in the visible rectangle of the
1146 * <code>JTree</code>). If false, when the selection
1147 * changes the nodes parent is not made visible (all its parents expanded).
1148 * This is useful if you wish to have your selection model maintain paths
1149 * that are not always visible (all parents expanded).
1150 * <p>
1151 * This is a bound property.
1152 *
1153 * @param newValue the new value for <code>expandsSelectedPaths</code>
1154 *
1155 * @since 1.3
1156 * @beaninfo
1157 * bound: true
1158 * description: Indicates whether changes to the selection should make
1159 * the parent of the path visible.
1160 */
1161 public void setExpandsSelectedPaths(boolean newValue) {
1162 boolean oldValue = expandsSelectedPaths;
1163
1164 expandsSelectedPaths = newValue;
1165 firePropertyChange(EXPANDS_SELECTED_PATHS_PROPERTY, oldValue,
1166 newValue);
1167 }
1168
1169 /**
1170 * Returns the <code>expandsSelectedPaths</code> property.
1171 * @return true if selection changes result in the parent path being
1172 * expanded
1173 * @since 1.3
1174 * @see #setExpandsSelectedPaths
1175 */
1176 public boolean getExpandsSelectedPaths() {
1177 return expandsSelectedPaths;
1178 }
1179
1180 /**
1181 * Turns on or off automatic drag handling. In order to enable automatic
1182 * drag handling, this property should be set to {@code true}, and the
1183 * tree's {@code TransferHandler} needs to be {@code non-null}.
1184 * The default value of the {@code dragEnabled} property is {@code false}.
1185 * <p>
1186 * The job of honoring this property, and recognizing a user drag gesture,
1187 * lies with the look and feel implementation, and in particular, the tree's
1188 * {@code TreeUI}. When automatic drag handling is enabled, most look and
1189 * feels (including those that subclass {@code BasicLookAndFeel}) begin a
1190 * drag and drop operation whenever the user presses the mouse button over
1191 * an item and then moves the mouse a few pixels. Setting this property to
1192 * {@code true} can therefore have a subtle effect on how selections behave.
1193 * <p>
1194 * If a look and feel is used that ignores this property, you can still
1195 * begin a drag and drop operation by calling {@code exportAsDrag} on the
1196 * tree's {@code TransferHandler}.
1197 *
1198 * @param b whether or not to enable automatic drag handling
1199 * @exception HeadlessException if
1200 * <code>b</code> is <code>true</code> and
1201 * <code>GraphicsEnvironment.isHeadless()</code>
1202 * returns <code>true</code>
1203 * @see java.awt.GraphicsEnvironment#isHeadless
1204 * @see #getDragEnabled
1205 * @see #setTransferHandler
1206 * @see TransferHandler
1207 * @since 1.4
1208 *
1209 * @beaninfo
1210 * description: determines whether automatic drag handling is enabled
1211 * bound: false
1212 */
1213 public void setDragEnabled(boolean b) {
1214 if (b && GraphicsEnvironment.isHeadless()) {
1215 throw new HeadlessException();
1216 }
1217 dragEnabled = b;
1218 }
1219
1220 /**
1221 * Returns whether or not automatic drag handling is enabled.
1222 *
1223 * @return the value of the {@code dragEnabled} property
1224 * @see #setDragEnabled
1225 * @since 1.4
1226 */
1227 public boolean getDragEnabled() {
1228 return dragEnabled;
1229 }
1230
1231 /**
1232 * Sets the drop mode for this component. For backward compatibility,
1233 * the default for this property is <code>DropMode.USE_SELECTION</code>.
1234 * Usage of one of the other modes is recommended, however, for an
1235 * improved user experience. <code>DropMode.ON</code>, for instance,
1236 * offers similar behavior of showing items as selected, but does so without
1237 * affecting the actual selection in the tree.
1238 * <p>
1239 * <code>JTree</code> supports the following drop modes:
1240 * <ul>
1241 * <li><code>DropMode.USE_SELECTION</code></li>
1242 * <li><code>DropMode.ON</code></li>
1243 * <li><code>DropMode.INSERT</code></li>
1244 * <li><code>DropMode.ON_OR_INSERT</code></li>
1245 * </ul>
1246 * <p>
1247 * The drop mode is only meaningful if this component has a
1248 * <code>TransferHandler</code> that accepts drops.
1249 *
1250 * @param dropMode the drop mode to use
1251 * @throws IllegalArgumentException if the drop mode is unsupported
1252 * or <code>null</code>
1253 * @see #getDropMode
1254 * @see #getDropLocation
1255 * @see #setTransferHandler
1256 * @see TransferHandler
1257 * @since 1.6
1258 */
1259 public final void setDropMode(DropMode dropMode) {
1260 if (dropMode != null) {
1261 switch (dropMode) {
1262 case USE_SELECTION:
1263 case ON:
1264 case INSERT:
1265 case ON_OR_INSERT:
1266 this.dropMode = dropMode;
1267 return;
1268 }
1269 }
1270
1271 throw new IllegalArgumentException(dropMode + ": Unsupported drop mode for tree");
1272 }
1273
1274 /**
1275 * Returns the drop mode for this component.
1276 *
1277 * @return the drop mode for this component
1278 * @see #setDropMode
1279 * @since 1.6
1280 */
1281 public final DropMode getDropMode() {
1282 return dropMode;
1283 }
1284
1285 /**
1286 * Calculates a drop location in this component, representing where a
1287 * drop at the given point should insert data.
1288 *
1289 * @param p the point to calculate a drop location for
1290 * @return the drop location, or <code>null</code>
1291 */
1292 DropLocation dropLocationForPoint(Point p) {
1293 DropLocation location = null;
1294
1295 int row = getClosestRowForLocation(p.x, p.y);
1296 Rectangle bounds = getRowBounds(row);
1297 TreeModel model = getModel();
1298 Object root = (model == null) ? null : model.getRoot();
1299 TreePath rootPath = (root == null) ? null : new TreePath(root);
1300
1301 TreePath child;
1302 TreePath parent;
1303 boolean outside = row == -1
1304 || p.y < bounds.y
1305 || p.y >= bounds.y + bounds.height;
1306
1307 switch(dropMode) {
1308 case USE_SELECTION:
1309 case ON:
1310 if (outside) {
1311 location = new DropLocation(p, null, -1);
1312 } else {
1313 location = new DropLocation(p, getPathForRow(row), -1);
1314 }
1315
1316 break;
1317 case INSERT:
1318 case ON_OR_INSERT:
1319 if (row == -1) {
1320 if (root != null && !model.isLeaf(root) && isExpanded(rootPath)) {
1321 location = new DropLocation(p, rootPath, 0);
1322 } else {
1323 location = new DropLocation(p, null, -1);
1324 }
1325
1326 break;
1327 }
1328
1329 boolean checkOn = dropMode == DropMode.ON_OR_INSERT
1330 || !model.isLeaf(getPathForRow(row).getLastPathComponent());
1331
1332 Section section = SwingUtilities2.liesInVertical(bounds, p, checkOn);
1333 if(section == LEADING) {
1334 child = getPathForRow(row);
1335 parent = child.getParentPath();
1336 } else if (section == TRAILING) {
1337 int index = row + 1;
1338 if (index >= getRowCount()) {
1339 if (model.isLeaf(root) || !isExpanded(rootPath)) {
1340 location = new DropLocation(p, null, -1);
1341 } else {
1342 parent = rootPath;
1343 index = model.getChildCount(root);
1344 location = new DropLocation(p, parent, index);
1345 }
1346
1347 break;
1348 }
1349
1350 child = getPathForRow(index);
1351 parent = child.getParentPath();
1352 } else {
1353 assert checkOn;
1354 location = new DropLocation(p, getPathForRow(row), -1);
1355 break;
1356 }
1357
1358 if (parent != null) {
1359 location = new DropLocation(p, parent,
1360 model.getIndexOfChild(parent.getLastPathComponent(),
1361 child.getLastPathComponent()));
1362 } else if (checkOn || !model.isLeaf(root)) {
1363 location = new DropLocation(p, rootPath, -1);
1364 } else {
1365 location = new DropLocation(p, null, -1);
1366 }
1367
1368 break;
1369 default:
1370 assert false : "Unexpected drop mode";
1371 }
1372
1373 if (outside || row != expandRow) {
1374 cancelDropTimer();
1375 }
1376
1377 if (!outside && row != expandRow) {
1378 if (isCollapsed(row)) {
1379 expandRow = row;
1380 startDropTimer();
1381 }
1382 }
1383
1384 return location;
1385 }
1386
1387 /**
1388 * Called to set or clear the drop location during a DnD operation.
1389 * In some cases, the component may need to use it's internal selection
1390 * temporarily to indicate the drop location. To help facilitate this,
1391 * this method returns and accepts as a parameter a state object.
1392 * This state object can be used to store, and later restore, the selection
1393 * state. Whatever this method returns will be passed back to it in
1394 * future calls, as the state parameter. If it wants the DnD system to
1395 * continue storing the same state, it must pass it back every time.
1396 * Here's how this is used:
1397 * <p>
1398 * Let's say that on the first call to this method the component decides
1399 * to save some state (because it is about to use the selection to show
1400 * a drop index). It can return a state object to the caller encapsulating
1401 * any saved selection state. On a second call, let's say the drop location
1402 * is being changed to something else. The component doesn't need to
1403 * restore anything yet, so it simply passes back the same state object
1404 * to have the DnD system continue storing it. Finally, let's say this
1405 * method is messaged with <code>null</code>. This means DnD
1406 * is finished with this component for now, meaning it should restore
1407 * state. At this point, it can use the state parameter to restore
1408 * said state, and of course return <code>null</code> since there's
1409 * no longer anything to store.
1410 *
1411 * @param location the drop location (as calculated by
1412 * <code>dropLocationForPoint</code>) or <code>null</code>
1413 * if there's no longer a valid drop location
1414 * @param state the state object saved earlier for this component,
1415 * or <code>null</code>
1416 * @param forDrop whether or not the method is being called because an
1417 * actual drop occurred
1418 * @return any saved state for this component, or <code>null</code> if none
1419 */
1420 Object setDropLocation(TransferHandler.DropLocation location,
1421 Object state,
1422 boolean forDrop) {
1423
1424 Object retVal = null;
1425 DropLocation treeLocation = (DropLocation)location;
1426
1427 if (dropMode == DropMode.USE_SELECTION) {
1428 if (treeLocation == null) {
1429 if (!forDrop && state != null) {
1430 setSelectionPaths(((TreePath[][])state)[0]);
1431 setAnchorSelectionPath(((TreePath[][])state)[1][0]);
1432 setLeadSelectionPath(((TreePath[][])state)[1][1]);
1433 }
1434 } else {
1435 if (dropLocation == null) {
1436 TreePath[] paths = getSelectionPaths();
1437 if (paths == null) {
1438 paths = new TreePath[0];
1439 }
1440
1441 retVal = new TreePath[][] {paths,
1442 {getAnchorSelectionPath(), getLeadSelectionPath()}};
1443 } else {
1444 retVal = state;
1445 }
1446
1447 setSelectionPath(treeLocation.getPath());
1448 }
1449 }
1450
1451 DropLocation old = dropLocation;
1452 dropLocation = treeLocation;
1453 firePropertyChange("dropLocation", old, dropLocation);
1454
1455 return retVal;
1456 }
1457
1458 /**
1459 * Called to indicate to this component that DnD is done.
1460 * Allows for us to cancel the expand timer.
1461 */
1462 void dndDone() {
1463 cancelDropTimer();
1464 dropTimer = null;
1465 }
1466
1467 /**
1468 * Returns the location that this component should visually indicate
1469 * as the drop location during a DnD operation over the component,
1470 * or {@code null} if no location is to currently be shown.
1471 * <p>
1472 * This method is not meant for querying the drop location
1473 * from a {@code TransferHandler}, as the drop location is only
1474 * set after the {@code TransferHandler}'s <code>canImport</code>
1475 * has returned and has allowed for the location to be shown.
1476 * <p>
1477 * When this property changes, a property change event with
1478 * name "dropLocation" is fired by the component.
1479 *
1480 * @return the drop location
1481 * @see #setDropMode
1482 * @see TransferHandler#canImport(TransferHandler.TransferSupport)
1483 * @since 1.6
1484 */
1485 public final DropLocation getDropLocation() {
1486 return dropLocation;
1487 }
1488
1489 private void startDropTimer() {
1490 if (dropTimer == null) {
1491 dropTimer = new TreeTimer();
1492 }
1493 dropTimer.start();
1494 }
1495
1496 private void cancelDropTimer() {
1497 if (dropTimer != null && dropTimer.isRunning()) {
1498 expandRow = -1;
1499 dropTimer.stop();
1500 }
1501 }
1502
1503 /**
1504 * Returns <code>isEditable</code>. This is invoked from the UI before
1505 * editing begins to insure that the given path can be edited. This
1506 * is provided as an entry point for subclassers to add filtered
1507 * editing without having to resort to creating a new editor.
1508 *
1509 * @return true if every parent node and the node itself is editable
1510 * @see #isEditable
1511 */
1512 public boolean isPathEditable(TreePath path) {
1513 return isEditable();
1514 }
1515
1516 /**
1517 * Overrides <code>JComponent</code>'s <code>getToolTipText</code>
1518 * method in order to allow
1519 * renderer's tips to be used if it has text set.
1520 * <p>
1521 * NOTE: For <code>JTree</code> to properly display tooltips of its
1522 * renderers, <code>JTree</code> must be a registered component with the
1523 * <code>ToolTipManager</code>. This can be done by invoking
1524 * <code>ToolTipManager.sharedInstance().registerComponent(tree)</code>.
1525 * This is not done automatically!
1526 *
1527 * @param event the <code>MouseEvent</code> that initiated the
1528 * <code>ToolTip</code> display
1529 * @return a string containing the tooltip or <code>null</code>
1530 * if <code>event</code> is null
1531 */
1532 public String getToolTipText(MouseEvent event) {
1533 String tip = null;
1534
1535 if(event != null) {
1536 Point p = event.getPoint();
1537 int selRow = getRowForLocation(p.x, p.y);
1538 TreeCellRenderer r = getCellRenderer();
1539
1540 if(selRow != -1 && r != null) {
1541 TreePath path = getPathForRow(selRow);
1542 Object lastPath = path.getLastPathComponent();
1543 Component rComponent = r.getTreeCellRendererComponent
1544 (this, lastPath, isRowSelected(selRow),
1545 isExpanded(selRow), getModel().isLeaf(lastPath), selRow,
1546 true);
1547
1548 if(rComponent instanceof JComponent) {
1549 MouseEvent newEvent;
1550 Rectangle pathBounds = getPathBounds(path);
1551
1552 p.translate(-pathBounds.x, -pathBounds.y);
1553 newEvent = new MouseEvent(rComponent, event.getID(),
1554 event.getWhen(),
1555 event.getModifiers(),
1556 p.x, p.y,
1557 event.getXOnScreen(),
1558 event.getYOnScreen(),
1559 event.getClickCount(),
1560 event.isPopupTrigger(),
1561 MouseEvent.NOBUTTON);
1562
1563 tip = ((JComponent)rComponent).getToolTipText(newEvent);
1564 }
1565 }
1566 }
1567 // No tip from the renderer get our own tip
1568 if (tip == null) {
1569 tip = getToolTipText();
1570 }
1571 return tip;
1572 }
1573
1574 /**
1575 * Called by the renderers to convert the specified value to
1576 * text. This implementation returns <code>value.toString</code>, ignoring
1577 * all other arguments. To control the conversion, subclass this
1578 * method and use any of the arguments you need.
1579 *
1580 * @param value the <code>Object</code> to convert to text
1581 * @param selected true if the node is selected
1582 * @param expanded true if the node is expanded
1583 * @param leaf true if the node is a leaf node
1584 * @param row an integer specifying the node's display row, where 0 is
1585 * the first row in the display
1586 * @param hasFocus true if the node has the focus
1587 * @return the <code>String</code> representation of the node's value
1588 */
1589 public String convertValueToText(Object value, boolean selected,
1590 boolean expanded, boolean leaf, int row,
1591 boolean hasFocus) {
1592 if(value != null) {
1593 String sValue = value.toString();
1594 if (sValue != null) {
1595 return sValue;
1596 }
1597 }
1598 return "";
1599 }
1600
1601 //
1602 // The following are convenience methods that get forwarded to the
1603 // current TreeUI.
1604 //
1605
1606 /**
1607 * Returns the number of viewable nodes. A node is viewable if all of its
1608 * parents are expanded. The root is only included in this count if
1609 * {@code isRootVisible()} is {@code true}. This returns {@code 0} if
1610 * the UI has not been set.
1611 *
1612 * @return the number of viewable nodes
1613 */
1614 public int getRowCount() {
1615 TreeUI tree = getUI();
1616
1617 if(tree != null)
1618 return tree.getRowCount(this);
1619 return 0;
1620 }
1621
1622 /**
1623 * Selects the node identified by the specified path. If any
1624 * component of the path is hidden (under a collapsed node), and
1625 * <code>getExpandsSelectedPaths</code> is true it is
1626 * exposed (made viewable).
1627 *
1628 * @param path the <code>TreePath</code> specifying the node to select
1629 */
1630 public void setSelectionPath(TreePath path) {
1631 getSelectionModel().setSelectionPath(path);
1632 }
1633
1634 /**
1635 * Selects the nodes identified by the specified array of paths.
1636 * If any component in any of the paths is hidden (under a collapsed
1637 * node), and <code>getExpandsSelectedPaths</code> is true
1638 * it is exposed (made viewable).
1639 *
1640 * @param paths an array of <code>TreePath</code> objects that specifies
1641 * the nodes to select
1642 */
1643 public void setSelectionPaths(TreePath[] paths) {
1644 getSelectionModel().setSelectionPaths(paths);
1645 }
1646
1647 /**
1648 * Sets the path identifies as the lead. The lead may not be selected.
1649 * The lead is not maintained by <code>JTree</code>,
1650 * rather the UI will update it.
1651 * <p>
1652 * This is a bound property.
1653 *
1654 * @param newPath the new lead path
1655 * @since 1.3
1656 * @beaninfo
1657 * bound: true
1658 * description: Lead selection path
1659 */
1660 public void setLeadSelectionPath(TreePath newPath) {
1661 TreePath oldValue = leadPath;
1662
1663 leadPath = newPath;
1664 firePropertyChange(LEAD_SELECTION_PATH_PROPERTY, oldValue, newPath);
1665 }
1666
1667 /**
1668 * Sets the path identified as the anchor.
1669 * The anchor is not maintained by <code>JTree</code>, rather the UI will
1670 * update it.
1671 * <p>
1672 * This is a bound property.
1673 *
1674 * @param newPath the new anchor path
1675 * @since 1.3
1676 * @beaninfo
1677 * bound: true
1678 * description: Anchor selection path
1679 */
1680 public void setAnchorSelectionPath(TreePath newPath) {
1681 TreePath oldValue = anchorPath;
1682
1683 anchorPath = newPath;
1684 firePropertyChange(ANCHOR_SELECTION_PATH_PROPERTY, oldValue, newPath);
1685 }
1686
1687 /**
1688 * Selects the node at the specified row in the display.
1689 *
1690 * @param row the row to select, where 0 is the first row in
1691 * the display
1692 */
1693 public void setSelectionRow(int row) {
1694 int[] rows = { row };
1695
1696 setSelectionRows(rows);
1697 }
1698
1699 /**
1700 * Selects the nodes corresponding to each of the specified rows
1701 * in the display. If a particular element of <code>rows</code> is
1702 * < 0 or >= <code>getRowCount</code>, it will be ignored.
1703 * If none of the elements
1704 * in <code>rows</code> are valid rows, the selection will
1705 * be cleared. That is it will be as if <code>clearSelection</code>
1706 * was invoked.
1707 *
1708 * @param rows an array of ints specifying the rows to select,
1709 * where 0 indicates the first row in the display
1710 */
1711 public void setSelectionRows(int[] rows) {
1712 TreeUI ui = getUI();
1713
1714 if(ui != null && rows != null) {
1715 int numRows = rows.length;
1716 TreePath[] paths = new TreePath[numRows];
1717
1718 for(int counter = 0; counter < numRows; counter++) {
1719 paths[counter] = ui.getPathForRow(this, rows[counter]);
1720 }
1721 setSelectionPaths(paths);
1722 }
1723 }
1724
1725 /**
1726 * Adds the node identified by the specified <code>TreePath</code>
1727 * to the current selection. If any component of the path isn't
1728 * viewable, and <code>getExpandsSelectedPaths</code> is true it is
1729 * made viewable.
1730 * <p>
1731 * Note that <code>JTree</code> does not allow duplicate nodes to
1732 * exist as children under the same parent -- each sibling must be
1733 * a unique object.
1734 *
1735 * @param path the <code>TreePath</code> to add
1736 */
1737 public void addSelectionPath(TreePath path) {
1738 getSelectionModel().addSelectionPath(path);
1739 }
1740
1741 /**
1742 * Adds each path in the array of paths to the current selection. If
1743 * any component of any of the paths isn't viewable and
1744 * <code>getExpandsSelectedPaths</code> is true, it is
1745 * made viewable.
1746 * <p>
1747 * Note that <code>JTree</code> does not allow duplicate nodes to
1748 * exist as children under the same parent -- each sibling must be
1749 * a unique object.
1750 *
1751 * @param paths an array of <code>TreePath</code> objects that specifies
1752 * the nodes to add
1753 */
1754 public void addSelectionPaths(TreePath[] paths) {
1755 getSelectionModel().addSelectionPaths(paths);
1756 }
1757
1758 /**
1759 * Adds the path at the specified row to the current selection.
1760 *
1761 * @param row an integer specifying the row of the node to add,
1762 * where 0 is the first row in the display
1763 */
1764 public void addSelectionRow(int row) {
1765 int[] rows = { row };
1766
1767 addSelectionRows(rows);
1768 }
1769
1770 /**
1771 * Adds the paths at each of the specified rows to the current selection.
1772 *
1773 * @param rows an array of ints specifying the rows to add,
1774 * where 0 indicates the first row in the display
1775 */
1776 public void addSelectionRows(int[] rows) {
1777 TreeUI ui = getUI();
1778
1779 if(ui != null && rows != null) {
1780 int numRows = rows.length;
1781 TreePath[] paths = new TreePath[numRows];
1782
1783 for(int counter = 0; counter < numRows; counter++)
1784 paths[counter] = ui.getPathForRow(this, rows[counter]);
1785 addSelectionPaths(paths);
1786 }
1787 }
1788
1789 /**
1790 * Returns the last path component of the selected path. This is
1791 * a convenience method for
1792 * {@code getSelectionModel().getSelectionPath().getLastPathComponent()}.
1793 * This is typically only useful if the selection has one path.
1794 *
1795 * @return the last path component of the selected path, or
1796 * <code>null</code> if nothing is selected
1797 * @see TreePath#getLastPathComponent
1798 */
1799 public Object getLastSelectedPathComponent() {
1800 TreePath selPath = getSelectionModel().getSelectionPath();
1801
1802 if(selPath != null)
1803 return selPath.getLastPathComponent();
1804 return null;
1805 }
1806
1807 /**
1808 * Returns the path identified as the lead.
1809 * @return path identified as the lead
1810 */
1811 public TreePath getLeadSelectionPath() {
1812 return leadPath;
1813 }
1814
1815 /**
1816 * Returns the path identified as the anchor.
1817 * @return path identified as the anchor
1818 * @since 1.3
1819 */
1820 public TreePath getAnchorSelectionPath() {
1821 return anchorPath;
1822 }
1823
1824 /**
1825 * Returns the path to the first selected node.
1826 *
1827 * @return the <code>TreePath</code> for the first selected node,
1828 * or <code>null</code> if nothing is currently selected
1829 */
1830 public TreePath getSelectionPath() {
1831 return getSelectionModel().getSelectionPath();
1832 }
1833
1834 /**
1835 * Returns the paths of all selected values.
1836 *
1837 * @return an array of <code>TreePath</code> objects indicating the selected
1838 * nodes, or <code>null</code> if nothing is currently selected
1839 */
1840 public TreePath[] getSelectionPaths() {
1841 return getSelectionModel().getSelectionPaths();
1842 }
1843
1844 /**
1845 * Returns all of the currently selected rows. This method is simply
1846 * forwarded to the <code>TreeSelectionModel</code>.
1847 * If nothing is selected <code>null</code> or an empty array will
1848 * be returned, based on the <code>TreeSelectionModel</code>
1849 * implementation.
1850 *
1851 * @return an array of integers that identifies all currently selected rows
1852 * where 0 is the first row in the display
1853 */
1854 public int[] getSelectionRows() {
1855 return getSelectionModel().getSelectionRows();
1856 }
1857
1858 /**
1859 * Returns the number of nodes selected.
1860 *
1861 * @return the number of nodes selected
1862 */
1863 public int getSelectionCount() {
1864 return selectionModel.getSelectionCount();
1865 }
1866
1867 /**
1868 * Returns the smallest selected row. If the selection is empty, or
1869 * none of the selected paths are viewable, {@code -1} is returned.
1870 *
1871 * @return the smallest selected row
1872 */
1873 public int getMinSelectionRow() {
1874 return getSelectionModel().getMinSelectionRow();
1875 }
1876
1877 /**
1878 * Returns the largest selected row. If the selection is empty, or
1879 * none of the selected paths are viewable, {@code -1} is returned.
1880 *
1881 * @return the largest selected row
1882 */
1883 public int getMaxSelectionRow() {
1884 return getSelectionModel().getMaxSelectionRow();
1885 }
1886
1887 /**
1888 * Returns the row index corresponding to the lead path.
1889 *
1890 * @return an integer giving the row index of the lead path,
1891 * where 0 is the first row in the display; or -1
1892 * if <code>leadPath</code> is <code>null</code>
1893 */
1894 public int getLeadSelectionRow() {
1895 TreePath leadPath = getLeadSelectionPath();
1896
1897 if (leadPath != null) {
1898 return getRowForPath(leadPath);
1899 }
1900 return -1;
1901 }
1902
1903 /**
1904 * Returns true if the item identified by the path is currently selected.
1905 *
1906 * @param path a <code>TreePath</code> identifying a node
1907 * @return true if the node is selected
1908 */
1909 public boolean isPathSelected(TreePath path) {
1910 return getSelectionModel().isPathSelected(path);
1911 }
1912
1913 /**
1914 * Returns true if the node identified by row is selected.
1915 *
1916 * @param row an integer specifying a display row, where 0 is the first
1917 * row in the display
1918 * @return true if the node is selected
1919 */
1920 public boolean isRowSelected(int row) {
1921 return getSelectionModel().isRowSelected(row);
1922 }
1923
1924 /**
1925 * Returns an <code>Enumeration</code> of the descendants of the
1926 * path <code>parent</code> that
1927 * are currently expanded. If <code>parent</code> is not currently
1928 * expanded, this will return <code>null</code>.
1929 * If you expand/collapse nodes while
1930 * iterating over the returned <code>Enumeration</code>
1931 * this may not return all
1932 * the expanded paths, or may return paths that are no longer expanded.
1933 *
1934 * @param parent the path which is to be examined
1935 * @return an <code>Enumeration</code> of the descendents of
1936 * <code>parent</code>, or <code>null</code> if
1937 * <code>parent</code> is not currently expanded
1938 */
1939 public Enumeration<TreePath> getExpandedDescendants(TreePath parent) {
1940 if(!isExpanded(parent))
1941 return null;
1942
1943 Enumeration<TreePath> toggledPaths = expandedState.keys();
1944 Vector<TreePath> elements = null;
1945 TreePath path;
1946 Object value;
1947
1948 if(toggledPaths != null) {
1949 while(toggledPaths.hasMoreElements()) {
1950 path = toggledPaths.nextElement();
1951 value = expandedState.get(path);
1952 // Add the path if it is expanded, a descendant of parent,
1953 // and it is visible (all parents expanded). This is rather
1954 // expensive!
1955 if(path != parent && value != null &&
1956 ((Boolean)value).booleanValue() &&
1957 parent.isDescendant(path) && isVisible(path)) {
1958 if (elements == null) {
1959 elements = new Vector<TreePath>();
1960 }
1961 elements.addElement(path);
1962 }
1963 }
1964 }
1965 if (elements == null) {
1966 Set<TreePath> empty = Collections.emptySet();
1967 return Collections.enumeration(empty);
1968 }
1969 return elements.elements();
1970 }
1971
1972 /**
1973 * Returns true if the node identified by the path has ever been
1974 * expanded.
1975 * @return true if the <code>path</code> has ever been expanded
1976 */
1977 public boolean hasBeenExpanded(TreePath path) {
1978 return (path != null && expandedState.get(path) != null);
1979 }
1980
1981 /**
1982 * Returns true if the node identified by the path is currently expanded,
1983 *
1984 * @param path the <code>TreePath</code> specifying the node to check
1985 * @return false if any of the nodes in the node's path are collapsed,
1986 * true if all nodes in the path are expanded
1987 */
1988 public boolean isExpanded(TreePath path) {
1989
1990 if(path == null)
1991 return false;
1992 Object value;
1993
1994 do{
1995 value = expandedState.get(path);
1996 if(value == null || !((Boolean)value).booleanValue())
1997 return false;
1998 } while( (path=path.getParentPath())!=null );
1999
2000 return true;
2001 }
2002
2003 /**
2004 * Returns true if the node at the specified display row is currently
2005 * expanded.
2006 *
2007 * @param row the row to check, where 0 is the first row in the
2008 * display
2009 * @return true if the node is currently expanded, otherwise false
2010 */
2011 public boolean isExpanded(int row) {
2012 TreeUI tree = getUI();
2013
2014 if(tree != null) {
2015 TreePath path = tree.getPathForRow(this, row);
2016
2017 if(path != null) {
2018 Boolean value = expandedState.get(path);
2019
2020 return (value != null && value.booleanValue());
2021 }
2022 }
2023 return false;
2024 }
2025
2026 /**
2027 * Returns true if the value identified by path is currently collapsed,
2028 * this will return false if any of the values in path are currently
2029 * not being displayed.
2030 *
2031 * @param path the <code>TreePath</code> to check
2032 * @return true if any of the nodes in the node's path are collapsed,
2033 * false if all nodes in the path are expanded
2034 */
2035 public boolean isCollapsed(TreePath path) {
2036 return !isExpanded(path);
2037 }
2038
2039 /**
2040 * Returns true if the node at the specified display row is collapsed.
2041 *
2042 * @param row the row to check, where 0 is the first row in the
2043 * display
2044 * @return true if the node is currently collapsed, otherwise false
2045 */
2046 public boolean isCollapsed(int row) {
2047 return !isExpanded(row);
2048 }
2049
2050 /**
2051 * Ensures that the node identified by path is currently viewable.
2052 *
2053 * @param path the <code>TreePath</code> to make visible
2054 */
2055 public void makeVisible(TreePath path) {
2056 if(path != null) {
2057 TreePath parentPath = path.getParentPath();
2058
2059 if(parentPath != null) {
2060 expandPath(parentPath);
2061 }
2062 }
2063 }
2064
2065 /**
2066 * Returns true if the value identified by path is currently viewable,
2067 * which means it is either the root or all of its parents are expanded.
2068 * Otherwise, this method returns false.
2069 *
2070 * @return true if the node is viewable, otherwise false
2071 */
2072 public boolean isVisible(TreePath path) {
2073 if(path != null) {
2074 TreePath parentPath = path.getParentPath();
2075
2076 if(parentPath != null)
2077 return isExpanded(parentPath);
2078 // Root.
2079 return true;
2080 }
2081 return false;
2082 }
2083
2084 /**
2085 * Returns the <code>Rectangle</code> that the specified node will be drawn
2086 * into. Returns <code>null</code> if any component in the path is hidden
2087 * (under a collapsed parent).
2088 * <p>
2089 * Note:<br>
2090 * This method returns a valid rectangle, even if the specified
2091 * node is not currently displayed.
2092 *
2093 * @param path the <code>TreePath</code> identifying the node
2094 * @return the <code>Rectangle</code> the node is drawn in,
2095 * or <code>null</code>
2096 */
2097 public Rectangle getPathBounds(TreePath path) {
2098 TreeUI tree = getUI();
2099
2100 if(tree != null)
2101 return tree.getPathBounds(this, path);
2102 return null;
2103 }
2104
2105 /**
2106 * Returns the <code>Rectangle</code> that the node at the specified row is
2107 * drawn in.
2108 *
2109 * @param row the row to be drawn, where 0 is the first row in the
2110 * display
2111 * @return the <code>Rectangle</code> the node is drawn in
2112 */
2113 public Rectangle getRowBounds(int row) {
2114 return getPathBounds(getPathForRow(row));
2115 }
2116
2117 /**
2118 * Makes sure all the path components in path are expanded (except
2119 * for the last path component) and scrolls so that the
2120 * node identified by the path is displayed. Only works when this
2121 * <code>JTree</code> is contained in a <code>JScrollPane</code>.
2122 *
2123 * @param path the <code>TreePath</code> identifying the node to
2124 * bring into view
2125 */
2126 public void scrollPathToVisible(TreePath path) {
2127 if(path != null) {
2128 makeVisible(path);
2129
2130 Rectangle bounds = getPathBounds(path);
2131
2132 if(bounds != null) {
2133 scrollRectToVisible(bounds);
2134 if (accessibleContext != null) {
2135 ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
2136 }
2137 }
2138 }
2139 }
2140
2141 /**
2142 * Scrolls the item identified by row until it is displayed. The minimum
2143 * of amount of scrolling necessary to bring the row into view
2144 * is performed. Only works when this <code>JTree</code> is contained in a
2145 * <code>JScrollPane</code>.
2146 *
2147 * @param row an integer specifying the row to scroll, where 0 is the
2148 * first row in the display
2149 */
2150 public void scrollRowToVisible(int row) {
2151 scrollPathToVisible(getPathForRow(row));
2152 }
2153
2154 /**
2155 * Returns the path for the specified row. If <code>row</code> is
2156 * not visible, or a {@code TreeUI} has not been set, <code>null</code>
2157 * is returned.
2158 *
2159 * @param row an integer specifying a row
2160 * @return the <code>TreePath</code> to the specified node,
2161 * <code>null</code> if <code>row < 0</code>
2162 * or <code>row >= getRowCount()</code>
2163 */
2164 public TreePath getPathForRow(int row) {
2165 TreeUI tree = getUI();
2166
2167 if(tree != null)
2168 return tree.getPathForRow(this, row);
2169 return null;
2170 }
2171
2172 /**
2173 * Returns the row that displays the node identified by the specified
2174 * path.
2175 *
2176 * @param path the <code>TreePath</code> identifying a node
2177 * @return an integer specifying the display row, where 0 is the first
2178 * row in the display, or -1 if any of the elements in path
2179 * are hidden under a collapsed parent.
2180 */
2181 public int getRowForPath(TreePath path) {
2182 TreeUI tree = getUI();
2183
2184 if(tree != null)
2185 return tree.getRowForPath(this, path);
2186 return -1;
2187 }
2188
2189 /**
2190 * Ensures that the node identified by the specified path is
2191 * expanded and viewable. If the last item in the path is a
2192 * leaf, this will have no effect.
2193 *
2194 * @param path the <code>TreePath</code> identifying a node
2195 */
2196 public void expandPath(TreePath path) {
2197 // Only expand if not leaf!
2198 TreeModel model = getModel();
2199
2200 if(path != null && model != null &&
2201 !model.isLeaf(path.getLastPathComponent())) {
2202 setExpandedState(path, true);
2203 }
2204 }
2205
2206 /**
2207 * Ensures that the node in the specified row is expanded and
2208 * viewable.
2209 * <p>
2210 * If <code>row</code> is < 0 or >= <code>getRowCount</code> this
2211 * will have no effect.
2212 *
2213 * @param row an integer specifying a display row, where 0 is the
2214 * first row in the display
2215 */
2216 public void expandRow(int row) {
2217 expandPath(getPathForRow(row));
2218 }
2219
2220 /**
2221 * Ensures that the node identified by the specified path is
2222 * collapsed and viewable.
2223 *
2224 * @param path the <code>TreePath</code> identifying a node
2225 */
2226 public void collapsePath(TreePath path) {
2227 setExpandedState(path, false);
2228 }
2229
2230 /**
2231 * Ensures that the node in the specified row is collapsed.
2232 * <p>
2233 * If <code>row</code> is < 0 or >= <code>getRowCount</code> this
2234 * will have no effect.
2235 *
2236 * @param row an integer specifying a display row, where 0 is the
2237 * first row in the display
2238 */
2239 public void collapseRow(int row) {
2240 collapsePath(getPathForRow(row));
2241 }
2242
2243 /**
2244 * Returns the path for the node at the specified location.
2245 *
2246 * @param x an integer giving the number of pixels horizontally from
2247 * the left edge of the display area, minus any left margin
2248 * @param y an integer giving the number of pixels vertically from
2249 * the top of the display area, minus any top margin
2250 * @return the <code>TreePath</code> for the node at that location
2251 */
2252 public TreePath getPathForLocation(int x, int y) {
2253 TreePath closestPath = getClosestPathForLocation(x, y);
2254
2255 if(closestPath != null) {
2256 Rectangle pathBounds = getPathBounds(closestPath);
2257
2258 if(pathBounds != null &&
2259 x >= pathBounds.x && x < (pathBounds.x + pathBounds.width) &&
2260 y >= pathBounds.y && y < (pathBounds.y + pathBounds.height))
2261 return closestPath;
2262 }
2263 return null;
2264 }
2265
2266 /**
2267 * Returns the row for the specified location.
2268 *
2269 * @param x an integer giving the number of pixels horizontally from
2270 * the left edge of the display area, minus any left margin
2271 * @param y an integer giving the number of pixels vertically from
2272 * the top of the display area, minus any top margin
2273 * @return the row corresponding to the location, or -1 if the
2274 * location is not within the bounds of a displayed cell
2275 * @see #getClosestRowForLocation
2276 */
2277 public int getRowForLocation(int x, int y) {
2278 return getRowForPath(getPathForLocation(x, y));
2279 }
2280
2281 /**
2282 * Returns the path to the node that is closest to x,y. If
2283 * no nodes are currently viewable, or there is no model, returns
2284 * <code>null</code>, otherwise it always returns a valid path. To test if
2285 * the node is exactly at x, y, get the node's bounds and
2286 * test x, y against that.
2287 *
2288 * @param x an integer giving the number of pixels horizontally from
2289 * the left edge of the display area, minus any left margin
2290 * @param y an integer giving the number of pixels vertically from
2291 * the top of the display area, minus any top margin
2292 * @return the <code>TreePath</code> for the node closest to that location,
2293 * <code>null</code> if nothing is viewable or there is no model
2294 *
2295 * @see #getPathForLocation
2296 * @see #getPathBounds
2297 */
2298 public TreePath getClosestPathForLocation(int x, int y) {
2299 TreeUI tree = getUI();
2300
2301 if(tree != null)
2302 return tree.getClosestPathForLocation(this, x, y);
2303 return null;
2304 }
2305
2306 /**
2307 * Returns the row to the node that is closest to x,y. If no nodes
2308 * are viewable or there is no model, returns -1. Otherwise,
2309 * it always returns a valid row. To test if the returned object is
2310 * exactly at x, y, get the bounds for the node at the returned
2311 * row and test x, y against that.
2312 *
2313 * @param x an integer giving the number of pixels horizontally from
2314 * the left edge of the display area, minus any left margin
2315 * @param y an integer giving the number of pixels vertically from
2316 * the top of the display area, minus any top margin
2317 * @return the row closest to the location, -1 if nothing is
2318 * viewable or there is no model
2319 *
2320 * @see #getRowForLocation
2321 * @see #getRowBounds
2322 */
2323 public int getClosestRowForLocation(int x, int y) {
2324 return getRowForPath(getClosestPathForLocation(x, y));
2325 }
2326
2327 /**
2328 * Returns true if the tree is being edited. The item that is being
2329 * edited can be obtained using <code>getSelectionPath</code>.
2330 *
2331 * @return true if the user is currently editing a node
2332 * @see #getSelectionPath
2333 */
2334 public boolean isEditing() {
2335 TreeUI tree = getUI();
2336
2337 if(tree != null)
2338 return tree.isEditing(this);
2339 return false;
2340 }
2341
2342 /**
2343 * Ends the current editing session.
2344 * (The <code>DefaultTreeCellEditor</code>
2345 * object saves any edits that are currently in progress on a cell.
2346 * Other implementations may operate differently.)
2347 * Has no effect if the tree isn't being edited.
2348 * <blockquote>
2349 * <b>Note:</b><br>
2350 * To make edit-saves automatic whenever the user changes
2351 * their position in the tree, use {@link #setInvokesStopCellEditing}.
2352 * </blockquote>
2353 *
2354 * @return true if editing was in progress and is now stopped,
2355 * false if editing was not in progress
2356 */
2357 public boolean stopEditing() {
2358 TreeUI tree = getUI();
2359
2360 if(tree != null)
2361 return tree.stopEditing(this);
2362 return false;
2363 }
2364
2365 /**
2366 * Cancels the current editing session. Has no effect if the
2367 * tree isn't being edited.
2368 */
2369 public void cancelEditing() {
2370 TreeUI tree = getUI();
2371
2372 if(tree != null)
2373 tree.cancelEditing(this);
2374 }
2375
2376 /**
2377 * Selects the node identified by the specified path and initiates
2378 * editing. The edit-attempt fails if the <code>CellEditor</code>
2379 * does not allow
2380 * editing for the specified item.
2381 *
2382 * @param path the <code>TreePath</code> identifying a node
2383 */
2384 public void startEditingAtPath(TreePath path) {
2385 TreeUI tree = getUI();
2386
2387 if(tree != null)
2388 tree.startEditingAtPath(this, path);
2389 }
2390
2391 /**
2392 * Returns the path to the element that is currently being edited.
2393 *
2394 * @return the <code>TreePath</code> for the node being edited
2395 */
2396 public TreePath getEditingPath() {
2397 TreeUI tree = getUI();
2398
2399 if(tree != null)
2400 return tree.getEditingPath(this);
2401 return null;
2402 }
2403
2404 //
2405 // Following are primarily convenience methods for mapping from
2406 // row based selections to path selections. Sometimes it is
2407 // easier to deal with these than paths (mouse downs, key downs
2408 // usually just deal with index based selections).
2409 // Since row based selections require a UI many of these won't work
2410 // without one.
2411 //
2412
2413 /**
2414 * Sets the tree's selection model. When a <code>null</code> value is
2415 * specified an empty
2416 * <code>selectionModel</code> is used, which does not allow selections.
2417 * <p>
2418 * This is a bound property.
2419 *
2420 * @param selectionModel the <code>TreeSelectionModel</code> to use,
2421 * or <code>null</code> to disable selections
2422 * @see TreeSelectionModel
2423 * @beaninfo
2424 * bound: true
2425 * description: The tree's selection model.
2426 */
2427 public void setSelectionModel(TreeSelectionModel selectionModel) {
2428 if(selectionModel == null)
2429 selectionModel = EmptySelectionModel.sharedInstance();
2430
2431 TreeSelectionModel oldValue = this.selectionModel;
2432
2433 if (this.selectionModel != null && selectionRedirector != null) {
2434 this.selectionModel.removeTreeSelectionListener
2435 (selectionRedirector);
2436 }
2437 if (accessibleContext != null) {
2438 this.selectionModel.removeTreeSelectionListener((TreeSelectionListener)accessibleContext);
2439 selectionModel.addTreeSelectionListener((TreeSelectionListener)accessibleContext);
2440 }
2441
2442 this.selectionModel = selectionModel;
2443 if (selectionRedirector != null) {
2444 this.selectionModel.addTreeSelectionListener(selectionRedirector);
2445 }
2446 firePropertyChange(SELECTION_MODEL_PROPERTY, oldValue,
2447 this.selectionModel);
2448
2449 if (accessibleContext != null) {
2450 accessibleContext.firePropertyChange(
2451 AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
2452 Boolean.valueOf(false), Boolean.valueOf(true));
2453 }
2454 }
2455
2456 /**
2457 * Returns the model for selections. This should always return a
2458 * non-<code>null</code> value. If you don't want to allow anything
2459 * to be selected
2460 * set the selection model to <code>null</code>, which forces an empty
2461 * selection model to be used.
2462 *
2463 * @see #setSelectionModel
2464 */
2465 public TreeSelectionModel getSelectionModel() {
2466 return selectionModel;
2467 }
2468
2469 /**
2470 * Returns the paths (inclusive) between the specified rows. If
2471 * the specified indices are within the viewable set of rows, or
2472 * bound the viewable set of rows, then the indices are
2473 * constrained by the viewable set of rows. If the specified
2474 * indices are not within the viewable set of rows, or do not
2475 * bound the viewable set of rows, then an empty array is
2476 * returned. For example, if the row count is {@code 10}, and this
2477 * method is invoked with {@code -1, 20}, then the specified
2478 * indices are constrained to the viewable set of rows, and this is
2479 * treated as if invoked with {@code 0, 9}. On the other hand, if
2480 * this were invoked with {@code -10, -1}, then the specified
2481 * indices do not bound the viewable set of rows, and an empty
2482 * array is returned.
2483 * <p>
2484 * The parameters are not order dependent. That is, {@code
2485 * getPathBetweenRows(x, y)} is equivalent to
2486 * {@code getPathBetweenRows(y, x)}.
2487 * <p>
2488 * An empty array is returned if the row count is {@code 0}, or
2489 * the specified indices do not bound the viewable set of rows.
2490 *
2491 * @param index0 the first index in the range
2492 * @param index1 the last index in the range
2493 * @return the paths (inclusive) between the specified row indices
2494 */
2495 protected TreePath[] getPathBetweenRows(int index0, int index1) {
2496 TreeUI tree = getUI();
2497 if (tree != null) {
2498 int rowCount = getRowCount();
2499 if (rowCount > 0 && !((index0 < 0 && index1 < 0) ||
2500 (index0 >= rowCount && index1 >= rowCount))){
2501 index0 = Math.min(rowCount - 1, Math.max(index0, 0));
2502 index1 = Math.min(rowCount - 1, Math.max(index1, 0));
2503 int minIndex = Math.min(index0, index1);
2504 int maxIndex = Math.max(index0, index1);
2505 TreePath[] selection = new TreePath[
2506 maxIndex - minIndex + 1];
2507 for(int counter = minIndex; counter <= maxIndex; counter++) {
2508 selection[counter - minIndex] =
2509 tree.getPathForRow(this, counter);
2510 }
2511 return selection;
2512 }
2513 }
2514 return new TreePath[0];
2515 }
2516
2517 /**
2518 * Selects the rows in the specified interval (inclusive). If
2519 * the specified indices are within the viewable set of rows, or bound
2520 * the viewable set of rows, then the specified rows are constrained by
2521 * the viewable set of rows. If the specified indices are not within the
2522 * viewable set of rows, or do not bound the viewable set of rows, then
2523 * the selection is cleared. For example, if the row count is {@code
2524 * 10}, and this method is invoked with {@code -1, 20}, then the
2525 * specified indices bounds the viewable range, and this is treated as
2526 * if invoked with {@code 0, 9}. On the other hand, if this were
2527 * invoked with {@code -10, -1}, then the specified indices do not
2528 * bound the viewable set of rows, and the selection is cleared.
2529 * <p>
2530 * The parameters are not order dependent. That is, {@code
2531 * setSelectionInterval(x, y)} is equivalent to
2532 * {@code setSelectionInterval(y, x)}.
2533 *
2534 * @param index0 the first index in the range to select
2535 * @param index1 the last index in the range to select
2536 */
2537 public void setSelectionInterval(int index0, int index1) {
2538 TreePath[] paths = getPathBetweenRows(index0, index1);
2539
2540 this.getSelectionModel().setSelectionPaths(paths);
2541 }
2542
2543 /**
2544 * Adds the specified rows (inclusive) to the selection. If the
2545 * specified indices are within the viewable set of rows, or bound
2546 * the viewable set of rows, then the specified indices are
2547 * constrained by the viewable set of rows. If the indices are not
2548 * within the viewable set of rows, or do not bound the viewable
2549 * set of rows, then the selection is unchanged. For example, if
2550 * the row count is {@code 10}, and this method is invoked with
2551 * {@code -1, 20}, then the specified indices bounds the viewable
2552 * range, and this is treated as if invoked with {@code 0, 9}. On
2553 * the other hand, if this were invoked with {@code -10, -1}, then
2554 * the specified indices do not bound the viewable set of rows,
2555 * and the selection is unchanged.
2556 * <p>
2557 * The parameters are not order dependent. That is, {@code
2558 * addSelectionInterval(x, y)} is equivalent to
2559 * {@code addSelectionInterval(y, x)}.
2560 *
2561 * @param index0 the first index in the range to add to the selection
2562 * @param index1 the last index in the range to add to the selection
2563 */
2564 public void addSelectionInterval(int index0, int index1) {
2565 TreePath[] paths = getPathBetweenRows(index0, index1);
2566
2567 if (paths != null && paths.length > 0) {
2568 this.getSelectionModel().addSelectionPaths(paths);
2569 }
2570 }
2571
2572 /**
2573 * Removes the specified rows (inclusive) from the selection. If
2574 * the specified indices are within the viewable set of rows, or bound
2575 * the viewable set of rows, then the specified indices are constrained by
2576 * the viewable set of rows. If the specified indices are not within the
2577 * viewable set of rows, or do not bound the viewable set of rows, then
2578 * the selection is unchanged. For example, if the row count is {@code
2579 * 10}, and this method is invoked with {@code -1, 20}, then the
2580 * specified range bounds the viewable range, and this is treated as
2581 * if invoked with {@code 0, 9}. On the other hand, if this were
2582 * invoked with {@code -10, -1}, then the specified range does not
2583 * bound the viewable set of rows, and the selection is unchanged.
2584 * <p>
2585 * The parameters are not order dependent. That is, {@code
2586 * removeSelectionInterval(x, y)} is equivalent to
2587 * {@code removeSelectionInterval(y, x)}.
2588 *
2589 * @param index0 the first row to remove from the selection
2590 * @param index1 the last row to remove from the selection
2591 */
2592 public void removeSelectionInterval(int index0, int index1) {
2593 TreePath[] paths = getPathBetweenRows(index0, index1);
2594
2595 if (paths != null && paths.length > 0) {
2596 this.getSelectionModel().removeSelectionPaths(paths);
2597 }
2598 }
2599
2600 /**
2601 * Removes the node identified by the specified path from the current
2602 * selection.
2603 *
2604 * @param path the <code>TreePath</code> identifying a node
2605 */
2606 public void removeSelectionPath(TreePath path) {
2607 this.getSelectionModel().removeSelectionPath(path);
2608 }
2609
2610 /**
2611 * Removes the nodes identified by the specified paths from the
2612 * current selection.
2613 *
2614 * @param paths an array of <code>TreePath</code> objects that
2615 * specifies the nodes to remove
2616 */
2617 public void removeSelectionPaths(TreePath[] paths) {
2618 this.getSelectionModel().removeSelectionPaths(paths);
2619 }
2620
2621 /**
2622 * Removes the row at the index <code>row</code> from the current
2623 * selection.
2624 *
2625 * @param row the row to remove
2626 */
2627 public void removeSelectionRow(int row) {
2628 int[] rows = { row };
2629
2630 removeSelectionRows(rows);
2631 }
2632
2633 /**
2634 * Removes the rows that are selected at each of the specified
2635 * rows.
2636 *
2637 * @param rows an array of ints specifying display rows, where 0 is
2638 * the first row in the display
2639 */
2640 public void removeSelectionRows(int[] rows) {
2641 TreeUI ui = getUI();
2642
2643 if(ui != null && rows != null) {
2644 int numRows = rows.length;
2645 TreePath[] paths = new TreePath[numRows];
2646
2647 for(int counter = 0; counter < numRows; counter++)
2648 paths[counter] = ui.getPathForRow(this, rows[counter]);
2649 removeSelectionPaths(paths);
2650 }
2651 }
2652
2653 /**
2654 * Clears the selection.
2655 */
2656 public void clearSelection() {
2657 getSelectionModel().clearSelection();
2658 }
2659
2660 /**
2661 * Returns true if the selection is currently empty.
2662 *
2663 * @return true if the selection is currently empty
2664 */
2665 public boolean isSelectionEmpty() {
2666 return getSelectionModel().isSelectionEmpty();
2667 }
2668
2669 /**
2670 * Adds a listener for <code>TreeExpansion</code> events.
2671 *
2672 * @param tel a TreeExpansionListener that will be notified when
2673 * a tree node is expanded or collapsed (a "negative
2674 * expansion")
2675 */
2676 public void addTreeExpansionListener(TreeExpansionListener tel) {
2677 if (settingUI) {
2678 uiTreeExpansionListener = tel;
2679 }
2680 listenerList.add(TreeExpansionListener.class, tel);
2681 }
2682
2683 /**
2684 * Removes a listener for <code>TreeExpansion</code> events.
2685 *
2686 * @param tel the <code>TreeExpansionListener</code> to remove
2687 */
2688 public void removeTreeExpansionListener(TreeExpansionListener tel) {
2689 listenerList.remove(TreeExpansionListener.class, tel);
2690 if (uiTreeExpansionListener == tel) {
2691 uiTreeExpansionListener = null;
2692 }
2693 }
2694
2695 /**
2696 * Returns an array of all the <code>TreeExpansionListener</code>s added
2697 * to this JTree with addTreeExpansionListener().
2698 *
2699 * @return all of the <code>TreeExpansionListener</code>s added or an empty
2700 * array if no listeners have been added
2701 * @since 1.4
2702 */
2703 public TreeExpansionListener[] getTreeExpansionListeners() {
2704 return listenerList.getListeners(TreeExpansionListener.class);
2705 }
2706
2707 /**
2708 * Adds a listener for <code>TreeWillExpand</code> events.
2709 *
2710 * @param tel a <code>TreeWillExpandListener</code> that will be notified
2711 * when a tree node will be expanded or collapsed (a "negative
2712 * expansion")
2713 */
2714 public void addTreeWillExpandListener(TreeWillExpandListener tel) {
2715 listenerList.add(TreeWillExpandListener.class, tel);
2716 }
2717
2718 /**
2719 * Removes a listener for <code>TreeWillExpand</code> events.
2720 *
2721 * @param tel the <code>TreeWillExpandListener</code> to remove
2722 */
2723 public void removeTreeWillExpandListener(TreeWillExpandListener tel) {
2724 listenerList.remove(TreeWillExpandListener.class, tel);
2725 }
2726
2727 /**
2728 * Returns an array of all the <code>TreeWillExpandListener</code>s added
2729 * to this JTree with addTreeWillExpandListener().
2730 *
2731 * @return all of the <code>TreeWillExpandListener</code>s added or an empty
2732 * array if no listeners have been added
2733 * @since 1.4
2734 */
2735 public TreeWillExpandListener[] getTreeWillExpandListeners() {
2736 return listenerList.getListeners(TreeWillExpandListener.class);
2737 }
2738
2739 /**
2740 * Notifies all listeners that have registered interest for
2741 * notification on this event type. The event instance
2742 * is lazily created using the <code>path</code> parameter.
2743 *
2744 * @param path the <code>TreePath</code> indicating the node that was
2745 * expanded
2746 * @see EventListenerList
2747 */
2748 public void fireTreeExpanded(TreePath path) {
2749 // Guaranteed to return a non-null array
2750 Object[] listeners = listenerList.getListenerList();
2751 TreeExpansionEvent e = null;
2752 if (uiTreeExpansionListener != null) {
2753 e = new TreeExpansionEvent(this, path);
2754 uiTreeExpansionListener.treeExpanded(e);
2755 }
2756 // Process the listeners last to first, notifying
2757 // those that are interested in this event
2758 for (int i = listeners.length-2; i>=0; i-=2) {
2759 if (listeners[i]==TreeExpansionListener.class &&
2760 listeners[i + 1] != uiTreeExpansionListener) {
2761 // Lazily create the event:
2762 if (e == null)
2763 e = new TreeExpansionEvent(this, path);
2764 ((TreeExpansionListener)listeners[i+1]).
2765 treeExpanded(e);
2766 }
2767 }
2768 }
2769
2770 /**
2771 * Notifies all listeners that have registered interest for
2772 * notification on this event type. The event instance
2773 * is lazily created using the <code>path</code> parameter.
2774 *
2775 * @param path the <code>TreePath</code> indicating the node that was
2776 * collapsed
2777 * @see EventListenerList
2778 */
2779 public void fireTreeCollapsed(TreePath path) {
2780 // Guaranteed to return a non-null array
2781 Object[] listeners = listenerList.getListenerList();
2782 TreeExpansionEvent e = null;
2783 if (uiTreeExpansionListener != null) {
2784 e = new TreeExpansionEvent(this, path);
2785 uiTreeExpansionListener.treeCollapsed(e);
2786 }
2787 // Process the listeners last to first, notifying
2788 // those that are interested in this event
2789 for (int i = listeners.length-2; i>=0; i-=2) {
2790 if (listeners[i]==TreeExpansionListener.class &&
2791 listeners[i + 1] != uiTreeExpansionListener) {
2792 // Lazily create the event:
2793 if (e == null)
2794 e = new TreeExpansionEvent(this, path);
2795 ((TreeExpansionListener)listeners[i+1]).
2796 treeCollapsed(e);
2797 }
2798 }
2799 }
2800
2801 /**
2802 * Notifies all listeners that have registered interest for
2803 * notification on this event type. The event instance
2804 * is lazily created using the <code>path</code> parameter.
2805 *
2806 * @param path the <code>TreePath</code> indicating the node that was
2807 * expanded
2808 * @see EventListenerList
2809 */
2810 public void fireTreeWillExpand(TreePath path) throws ExpandVetoException {
2811 // Guaranteed to return a non-null array
2812 Object[] listeners = listenerList.getListenerList();
2813 TreeExpansionEvent e = null;
2814 // Process the listeners last to first, notifying
2815 // those that are interested in this event
2816 for (int i = listeners.length-2; i>=0; i-=2) {
2817 if (listeners[i]==TreeWillExpandListener.class) {
2818 // Lazily create the event:
2819 if (e == null)
2820 e = new TreeExpansionEvent(this, path);
2821 ((TreeWillExpandListener)listeners[i+1]).
2822 treeWillExpand(e);
2823 }
2824 }
2825 }
2826
2827 /**
2828 * Notifies all listeners that have registered interest for
2829 * notification on this event type. The event instance
2830 * is lazily created using the <code>path</code> parameter.
2831 *
2832 * @param path the <code>TreePath</code> indicating the node that was
2833 * expanded
2834 * @see EventListenerList
2835 */
2836 public void fireTreeWillCollapse(TreePath path) throws ExpandVetoException {
2837 // Guaranteed to return a non-null array
2838 Object[] listeners = listenerList.getListenerList();
2839 TreeExpansionEvent e = null;
2840 // Process the listeners last to first, notifying
2841 // those that are interested in this event
2842 for (int i = listeners.length-2; i>=0; i-=2) {
2843 if (listeners[i]==TreeWillExpandListener.class) {
2844 // Lazily create the event:
2845 if (e == null)
2846 e = new TreeExpansionEvent(this, path);
2847 ((TreeWillExpandListener)listeners[i+1]).
2848 treeWillCollapse(e);
2849 }
2850 }
2851 }
2852
2853 /**
2854 * Adds a listener for <code>TreeSelection</code> events.
2855 *
2856 * @param tsl the <code>TreeSelectionListener</code> that will be notified
2857 * when a node is selected or deselected (a "negative
2858 * selection")
2859 */
2860 public void addTreeSelectionListener(TreeSelectionListener tsl) {
2861 listenerList.add(TreeSelectionListener.class,tsl);
2862 if(listenerList.getListenerCount(TreeSelectionListener.class) != 0
2863 && selectionRedirector == null) {
2864 selectionRedirector = new TreeSelectionRedirector();
2865 selectionModel.addTreeSelectionListener(selectionRedirector);
2866 }
2867 }
2868
2869 /**
2870 * Removes a <code>TreeSelection</code> listener.
2871 *
2872 * @param tsl the <code>TreeSelectionListener</code> to remove
2873 */
2874 public void removeTreeSelectionListener(TreeSelectionListener tsl) {
2875 listenerList.remove(TreeSelectionListener.class,tsl);
2876 if(listenerList.getListenerCount(TreeSelectionListener.class) == 0
2877 && selectionRedirector != null) {
2878 selectionModel.removeTreeSelectionListener
2879 (selectionRedirector);
2880 selectionRedirector = null;
2881 }
2882 }
2883
2884 /**
2885 * Returns an array of all the <code>TreeSelectionListener</code>s added
2886 * to this JTree with addTreeSelectionListener().
2887 *
2888 * @return all of the <code>TreeSelectionListener</code>s added or an empty
2889 * array if no listeners have been added
2890 * @since 1.4
2891 */
2892 public TreeSelectionListener[] getTreeSelectionListeners() {
2893 return listenerList.getListeners(TreeSelectionListener.class);
2894 }
2895
2896 /**
2897 * Notifies all listeners that have registered interest for
2898 * notification on this event type.
2899 *
2900 * @param e the <code>TreeSelectionEvent</code> to be fired;
2901 * generated by the
2902 * <code>TreeSelectionModel</code>
2903 * when a node is selected or deselected
2904 * @see EventListenerList
2905 */
2906 protected void fireValueChanged(TreeSelectionEvent e) {
2907 // Guaranteed to return a non-null array
2908 Object[] listeners = listenerList.getListenerList();
2909 // Process the listeners last to first, notifying
2910 // those that are interested in this event
2911 for (int i = listeners.length-2; i>=0; i-=2) {
2912 // TreeSelectionEvent e = null;
2913 if (listeners[i]==TreeSelectionListener.class) {
2914 // Lazily create the event:
2915 // if (e == null)
2916 // e = new ListSelectionEvent(this, firstIndex, lastIndex);
2917 ((TreeSelectionListener)listeners[i+1]).valueChanged(e);
2918 }
2919 }
2920 }
2921
2922 /**
2923 * Sent when the tree has changed enough that we need to resize
2924 * the bounds, but not enough that we need to remove the
2925 * expanded node set (e.g nodes were expanded or collapsed, or
2926 * nodes were inserted into the tree). You should never have to
2927 * invoke this, the UI will invoke this as it needs to.
2928 */
2929 public void treeDidChange() {
2930 revalidate();
2931 repaint();
2932 }
2933
2934 /**
2935 * Sets the number of rows that are to be displayed.
2936 * This will only work if the tree is contained in a
2937 * <code>JScrollPane</code>,
2938 * and will adjust the preferred size and size of that scrollpane.
2939 * <p>
2940 * This is a bound property.
2941 *
2942 * @param newCount the number of rows to display
2943 * @beaninfo
2944 * bound: true
2945 * description: The number of rows that are to be displayed.
2946 */
2947 public void setVisibleRowCount(int newCount) {
2948 int oldCount = visibleRowCount;
2949
2950 visibleRowCount = newCount;
2951 firePropertyChange(VISIBLE_ROW_COUNT_PROPERTY, oldCount,
2952 visibleRowCount);
2953 invalidate();
2954 if (accessibleContext != null) {
2955 ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
2956 }
2957 }
2958
2959 /**
2960 * Returns the number of rows that are displayed in the display area.
2961 *
2962 * @return the number of rows displayed
2963 */
2964 public int getVisibleRowCount() {
2965 return visibleRowCount;
2966 }
2967
2968 /**
2969 * Expands the root path, assuming the current TreeModel has been set.
2970 */
2971 private void expandRoot() {
2972 TreeModel model = getModel();
2973
2974 if(model != null && model.getRoot() != null) {
2975 expandPath(new TreePath(model.getRoot()));
2976 }
2977 }
2978
2979 /**
2980 * Returns the TreePath to the next tree element that
2981 * begins with a prefix. To handle the conversion of a
2982 * <code>TreePath</code> into a String, <code>convertValueToText</code>
2983 * is used.
2984 *
2985 * @param prefix the string to test for a match
2986 * @param startingRow the row for starting the search
2987 * @param bias the search direction, either
2988 * Position.Bias.Forward or Position.Bias.Backward.
2989 * @return the TreePath of the next tree element that
2990 * starts with the prefix; otherwise null
2991 * @exception IllegalArgumentException if prefix is null
2992 * or startingRow is out of bounds
2993 * @since 1.4
2994 */
2995 public TreePath getNextMatch(String prefix, int startingRow,
2996 Position.Bias bias) {
2997
2998 int max = getRowCount();
2999 if (prefix == null) {
3000 throw new IllegalArgumentException();
3001 }
3002 if (startingRow < 0 || startingRow >= max) {
3003 throw new IllegalArgumentException();
3004 }
3005 prefix = prefix.toUpperCase();
3006
3007 // start search from the next/previous element froom the
3008 // selected element
3009 int increment = (bias == Position.Bias.Forward) ? 1 : -1;
3010 int row = startingRow;
3011 do {
3012 TreePath path = getPathForRow(row);
3013 String text = convertValueToText(
3014 path.getLastPathComponent(), isRowSelected(row),
3015 isExpanded(row), true, row, false);
3016
3017 if (text.toUpperCase().startsWith(prefix)) {
3018 return path;
3019 }
3020 row = (row + increment + max) % max;
3021 } while (row != startingRow);
3022 return null;
3023 }
3024
3025 // Serialization support.
3026 private void writeObject(ObjectOutputStream s) throws IOException {
3027 Vector<Object> values = new Vector<Object>();
3028
3029 s.defaultWriteObject();
3030 // Save the cellRenderer, if its Serializable.
3031 if(cellRenderer != null && cellRenderer instanceof Serializable) {
3032 values.addElement("cellRenderer");
3033 values.addElement(cellRenderer);
3034 }
3035 // Save the cellEditor, if its Serializable.
3036 if(cellEditor != null && cellEditor instanceof Serializable) {
3037 values.addElement("cellEditor");
3038 values.addElement(cellEditor);
3039 }
3040 // Save the treeModel, if its Serializable.
3041 if(treeModel != null && treeModel instanceof Serializable) {
3042 values.addElement("treeModel");
3043 values.addElement(treeModel);
3044 }
3045 // Save the selectionModel, if its Serializable.
3046 if(selectionModel != null && selectionModel instanceof Serializable) {
3047 values.addElement("selectionModel");
3048 values.addElement(selectionModel);
3049 }
3050
3051 Object expandedData = getArchivableExpandedState();
3052
3053 if(expandedData != null) {
3054 values.addElement("expandedState");
3055 values.addElement(expandedData);
3056 }
3057
3058 s.writeObject(values);
3059 if (getUIClassID().equals(uiClassID)) {
3060 byte count = JComponent.getWriteObjCounter(this);
3061 JComponent.setWriteObjCounter(this, --count);
3062 if (count == 0 && ui != null) {
3063 ui.installUI(this);
3064 }
3065 }
3066 }
3067
3068 private void readObject(ObjectInputStream s)
3069 throws IOException, ClassNotFoundException {
3070 s.defaultReadObject();
3071
3072 // Create an instance of expanded state.
3073
3074 expandedState = new Hashtable<TreePath, Boolean>();
3075
3076 expandedStack = new Stack<Stack<TreePath>>();
3077
3078 Vector values = (Vector)s.readObject();
3079 int indexCounter = 0;
3080 int maxCounter = values.size();
3081
3082 if(indexCounter < maxCounter && values.elementAt(indexCounter).
3083 equals("cellRenderer")) {
3084 cellRenderer = (TreeCellRenderer)values.elementAt(++indexCounter);
3085 indexCounter++;
3086 }
3087 if(indexCounter < maxCounter && values.elementAt(indexCounter).
3088 equals("cellEditor")) {
3089 cellEditor = (TreeCellEditor)values.elementAt(++indexCounter);
3090 indexCounter++;
3091 }
3092 if(indexCounter < maxCounter && values.elementAt(indexCounter).
3093 equals("treeModel")) {
3094 treeModel = (TreeModel)values.elementAt(++indexCounter);
3095 indexCounter++;
3096 }
3097 if(indexCounter < maxCounter && values.elementAt(indexCounter).
3098 equals("selectionModel")) {
3099 selectionModel = (TreeSelectionModel)values.elementAt(++indexCounter);
3100 indexCounter++;
3101 }
3102 if(indexCounter < maxCounter && values.elementAt(indexCounter).
3103 equals("expandedState")) {
3104 unarchiveExpandedState(values.elementAt(++indexCounter));
3105 indexCounter++;
3106 }
3107 // Reinstall the redirector.
3108 if(listenerList.getListenerCount(TreeSelectionListener.class) != 0) {
3109 selectionRedirector = new TreeSelectionRedirector();
3110 selectionModel.addTreeSelectionListener(selectionRedirector);
3111 }
3112 // Listener to TreeModel.
3113 if(treeModel != null) {
3114 treeModelListener = createTreeModelListener();
3115 if(treeModelListener != null)
3116 treeModel.addTreeModelListener(treeModelListener);
3117 }
3118 }
3119
3120 /**
3121 * Returns an object that can be archived indicating what nodes are
3122 * expanded and what aren't. The objects from the model are NOT
3123 * written out.
3124 */
3125 private Object getArchivableExpandedState() {
3126 TreeModel model = getModel();
3127
3128 if(model != null) {
3129 Enumeration<TreePath> paths = expandedState.keys();
3130
3131 if(paths != null) {
3132 Vector<Object> state = new Vector<Object>();
3133
3134 while(paths.hasMoreElements()) {
3135 TreePath path = paths.nextElement();
3136 Object archivePath;
3137
3138 try {
3139 archivePath = getModelIndexsForPath(path);
3140 } catch (Error error) {
3141 archivePath = null;
3142 }
3143 if(archivePath != null) {
3144 state.addElement(archivePath);
3145 state.addElement(expandedState.get(path));
3146 }
3147 }
3148 return state;
3149 }
3150 }
3151 return null;
3152 }
3153
3154 /**
3155 * Updates the expanded state of nodes in the tree based on the
3156 * previously archived state <code>state</code>.
3157 */
3158 private void unarchiveExpandedState(Object state) {
3159 if(state instanceof Vector) {
3160 Vector paths = (Vector)state;
3161
3162 for(int counter = paths.size() - 1; counter >= 0; counter--) {
3163 Boolean eState = (Boolean)paths.elementAt(counter--);
3164 TreePath path;
3165
3166 try {
3167 path = getPathForIndexs((int[])paths.elementAt(counter));
3168 if(path != null)
3169 expandedState.put(path, eState);
3170 } catch (Error error) {}
3171 }
3172 }
3173 }
3174
3175 /**
3176 * Returns an array of integers specifying the indexs of the
3177 * components in the <code>path</code>. If <code>path</code> is
3178 * the root, this will return an empty array. If <code>path</code>
3179 * is <code>null</code>, <code>null</code> will be returned.
3180 */
3181 private int[] getModelIndexsForPath(TreePath path) {
3182 if(path != null) {
3183 TreeModel model = getModel();
3184 int count = path.getPathCount();
3185 int[] indexs = new int[count - 1];
3186 Object parent = model.getRoot();
3187
3188 for(int counter = 1; counter < count; counter++) {
3189 indexs[counter - 1] = model.getIndexOfChild
3190 (parent, path.getPathComponent(counter));
3191 parent = path.getPathComponent(counter);
3192 if(indexs[counter - 1] < 0)
3193 return null;
3194 }
3195 return indexs;
3196 }
3197 return null;
3198 }
3199
3200 /**
3201 * Returns a <code>TreePath</code> created by obtaining the children
3202 * for each of the indices in <code>indexs</code>. If <code>indexs</code>
3203 * or the <code>TreeModel</code> is <code>null</code>, it will return
3204 * <code>null</code>.
3205 */
3206 private TreePath getPathForIndexs(int[] indexs) {
3207 if(indexs == null)
3208 return null;
3209
3210 TreeModel model = getModel();
3211
3212 if(model == null)
3213 return null;
3214
3215 int count = indexs.length;
3216 Object parent = model.getRoot();
3217 TreePath parentPath = new TreePath(parent);
3218
3219 for(int counter = 0; counter < count; counter++) {
3220 parent = model.getChild(parent, indexs[counter]);
3221 if(parent == null)
3222 return null;
3223 parentPath = parentPath.pathByAddingChild(parent);
3224 }
3225 return parentPath;
3226 }
3227
3228 /**
3229 * <code>EmptySelectionModel</code> is a <code>TreeSelectionModel</code>
3230 * that does not allow anything to be selected.
3231 * <p>
3232 * <strong>Warning:</strong>
3233 * Serialized objects of this class will not be compatible with
3234 * future Swing releases. The current serialization support is
3235 * appropriate for short term storage or RMI between applications running
3236 * the same version of Swing. As of 1.4, support for long term storage
3237 * of all JavaBeans<sup><font size="-2">TM</font></sup>
3238 * has been added to the <code>java.beans</code> package.
3239 * Please see {@link java.beans.XMLEncoder}.
3240 */
3241 protected static class EmptySelectionModel extends
3242 DefaultTreeSelectionModel
3243 {
3244 /**
3245 * The single instance of {@code EmptySelectionModel}.
3246 */
3247 protected static final EmptySelectionModel sharedInstance =
3248 new EmptySelectionModel();
3249
3250 /**
3251 * Returns the single instance of {@code EmptySelectionModel}.
3252 *
3253 * @return single instance of {@code EmptySelectionModel}
3254 */
3255 static public EmptySelectionModel sharedInstance() {
3256 return sharedInstance;
3257 }
3258
3259 /**
3260 * This is overriden to do nothing; {@code EmptySelectionModel}
3261 * does not allow a selection.
3262 *
3263 * @param paths the paths to select; this is ignored
3264 */
3265 public void setSelectionPaths(TreePath[] paths) {}
3266
3267 /**
3268 * This is overriden to do nothing; {@code EmptySelectionModel}
3269 * does not allow a selection.
3270 *
3271 * @param paths the paths to add to the selection; this is ignored
3272 */
3273 public void addSelectionPaths(TreePath[] paths) {}
3274
3275 /**
3276 * This is overriden to do nothing; {@code EmptySelectionModel}
3277 * does not allow a selection.
3278 *
3279 * @param paths the paths to remove; this is ignored
3280 */
3281 public void removeSelectionPaths(TreePath[] paths) {}
3282
3283 /**
3284 * This is overriden to do nothing; {@code EmptySelectionModel}
3285 * does not allow a selection.
3286 *
3287 * @param mode the selection mode; this is ignored
3288 * @since 1.7
3289 */
3290 public void setSelectionMode(int mode) {
3291 }
3292
3293 /**
3294 * This is overriden to do nothing; {@code EmptySelectionModel}
3295 * does not allow a selection.
3296 *
3297 * @param mapper the {@code RowMapper} instance; this is ignored
3298 * @since 1.7
3299 */
3300 public void setRowMapper(RowMapper mapper) {
3301 }
3302
3303 /**
3304 * This is overriden to do nothing; {@code EmptySelectionModel}
3305 * does not allow a selection.
3306 *
3307 * @param listener the listener to add; this is ignored
3308 * @since 1.7
3309 */
3310 public void addTreeSelectionListener(TreeSelectionListener listener) {
3311 }
3312
3313 /**
3314 * This is overriden to do nothing; {@code EmptySelectionModel}
3315 * does not allow a selection.
3316 *
3317 * @param listener the listener to remove; this is ignored
3318 * @since 1.7
3319 */
3320 public void removeTreeSelectionListener(
3321 TreeSelectionListener listener) {
3322 }
3323
3324 /**
3325 * This is overriden to do nothing; {@code EmptySelectionModel}
3326 * does not allow a selection.
3327 *
3328 * @param listener the listener to add; this is ignored
3329 * @since 1.7
3330 */
3331 public void addPropertyChangeListener(
3332 PropertyChangeListener listener) {
3333 }
3334
3335 /**
3336 * This is overriden to do nothing; {@code EmptySelectionModel}
3337 * does not allow a selection.
3338 *
3339 * @param listener the listener to remove; this is ignored
3340 * @since 1.7
3341 */
3342 public void removePropertyChangeListener(
3343 PropertyChangeListener listener) {
3344 }
3345 }
3346
3347
3348 /**
3349 * Handles creating a new <code>TreeSelectionEvent</code> with the
3350 * <code>JTree</code> as the
3351 * source and passing it off to all the listeners.
3352 * <p>
3353 * <strong>Warning:</strong>
3354 * Serialized objects of this class will not be compatible with
3355 * future Swing releases. The current serialization support is
3356 * appropriate for short term storage or RMI between applications running
3357 * the same version of Swing. As of 1.4, support for long term storage
3358 * of all JavaBeans<sup><font size="-2">TM</font></sup>
3359 * has been added to the <code>java.beans</code> package.
3360 * Please see {@link java.beans.XMLEncoder}.
3361 */
3362 protected class TreeSelectionRedirector implements Serializable,
3363 TreeSelectionListener
3364 {
3365 /**
3366 * Invoked by the <code>TreeSelectionModel</code> when the
3367 * selection changes.
3368 *
3369 * @param e the <code>TreeSelectionEvent</code> generated by the
3370 * <code>TreeSelectionModel</code>
3371 */
3372 public void valueChanged(TreeSelectionEvent e) {
3373 TreeSelectionEvent newE;
3374
3375 newE = (TreeSelectionEvent)e.cloneWithSource(JTree.this);
3376 fireValueChanged(newE);
3377 }
3378 } // End of class JTree.TreeSelectionRedirector
3379
3380 //
3381 // Scrollable interface
3382 //
3383
3384 /**
3385 * Returns the preferred display size of a <code>JTree</code>. The height is
3386 * determined from <code>getVisibleRowCount</code> and the width
3387 * is the current preferred width.
3388 *
3389 * @return a <code>Dimension</code> object containing the preferred size
3390 */
3391 public Dimension getPreferredScrollableViewportSize() {
3392 int width = getPreferredSize().width;
3393 int visRows = getVisibleRowCount();
3394 int height = -1;
3395
3396 if(isFixedRowHeight())
3397 height = visRows * getRowHeight();
3398 else {
3399 TreeUI ui = getUI();
3400
3401 if (ui != null && visRows > 0) {
3402 int rc = ui.getRowCount(this);
3403
3404 if (rc >= visRows) {
3405 Rectangle bounds = getRowBounds(visRows - 1);
3406 if (bounds != null) {
3407 height = bounds.y + bounds.height;
3408 }
3409 }
3410 else if (rc > 0) {
3411 Rectangle bounds = getRowBounds(0);
3412 if (bounds != null) {
3413 height = bounds.height * visRows;
3414 }
3415 }
3416 }
3417 if (height == -1) {
3418 height = 16 * visRows;
3419 }
3420 }
3421 return new Dimension(width, height);
3422 }
3423
3424 /**
3425 * Returns the amount to increment when scrolling. The amount is
3426 * the height of the first displayed row that isn't completely in view
3427 * or, if it is totally displayed, the height of the next row in the
3428 * scrolling direction.
3429 *
3430 * @param visibleRect the view area visible within the viewport
3431 * @param orientation either <code>SwingConstants.VERTICAL</code>
3432 * or <code>SwingConstants.HORIZONTAL</code>
3433 * @param direction less than zero to scroll up/left,
3434 * greater than zero for down/right
3435 * @return the "unit" increment for scrolling in the specified direction
3436 * @see JScrollBar#setUnitIncrement(int)
3437 */
3438 public int getScrollableUnitIncrement(Rectangle visibleRect,
3439 int orientation, int direction) {
3440 if(orientation == SwingConstants.VERTICAL) {
3441 Rectangle rowBounds;
3442 int firstIndex = getClosestRowForLocation
3443 (0, visibleRect.y);
3444
3445 if(firstIndex != -1) {
3446 rowBounds = getRowBounds(firstIndex);
3447 if(rowBounds.y != visibleRect.y) {
3448 if(direction < 0) {
3449 // UP
3450 return Math.max(0, (visibleRect.y - rowBounds.y));
3451 }
3452 return (rowBounds.y + rowBounds.height - visibleRect.y);
3453 }
3454 if(direction < 0) { // UP
3455 if(firstIndex != 0) {
3456 rowBounds = getRowBounds(firstIndex - 1);
3457 return rowBounds.height;
3458 }
3459 }
3460 else {
3461 return rowBounds.height;
3462 }
3463 }
3464 return 0;
3465 }
3466 return 4;
3467 }
3468
3469
3470 /**
3471 * Returns the amount for a block increment, which is the height or
3472 * width of <code>visibleRect</code>, based on <code>orientation</code>.
3473 *
3474 * @param visibleRect the view area visible within the viewport
3475 * @param orientation either <code>SwingConstants.VERTICAL</code>
3476 * or <code>SwingConstants.HORIZONTAL</code>
3477 * @param direction less than zero to scroll up/left,
3478 * greater than zero for down/right.
3479 * @return the "block" increment for scrolling in the specified direction
3480 * @see JScrollBar#setBlockIncrement(int)
3481 */
3482 public int getScrollableBlockIncrement(Rectangle visibleRect,
3483 int orientation, int direction) {
3484 return (orientation == SwingConstants.VERTICAL) ? visibleRect.height :
3485 visibleRect.width;
3486 }
3487
3488 /**
3489 * Returns false to indicate that the width of the viewport does not
3490 * determine the width of the table, unless the preferred width of
3491 * the tree is smaller than the viewports width. In other words:
3492 * ensure that the tree is never smaller than its viewport.
3493 *
3494 * @return whether the tree should track the width of the viewport
3495 * @see Scrollable#getScrollableTracksViewportWidth
3496 */
3497 public boolean getScrollableTracksViewportWidth() {
3498 Container parent = SwingUtilities.getUnwrappedParent(this);
3499 if (parent instanceof JViewport) {
3500 return parent.getWidth() > getPreferredSize().width;
3501 }
3502 return false;
3503 }
3504
3505 /**
3506 * Returns false to indicate that the height of the viewport does not
3507 * determine the height of the table, unless the preferred height
3508 * of the tree is smaller than the viewports height. In other words:
3509 * ensure that the tree is never smaller than its viewport.
3510 *
3511 * @return whether the tree should track the height of the viewport
3512 * @see Scrollable#getScrollableTracksViewportHeight
3513 */
3514 public boolean getScrollableTracksViewportHeight() {
3515 Container parent = SwingUtilities.getUnwrappedParent(this);
3516 if (parent instanceof JViewport) {
3517 return parent.getHeight() > getPreferredSize().height;
3518 }
3519 return false;
3520 }
3521
3522 /**
3523 * Sets the expanded state of this <code>JTree</code>.
3524 * If <code>state</code> is
3525 * true, all parents of <code>path</code> and path are marked as
3526 * expanded. If <code>state</code> is false, all parents of
3527 * <code>path</code> are marked EXPANDED, but <code>path</code> itself
3528 * is marked collapsed.<p>
3529 * This will fail if a <code>TreeWillExpandListener</code> vetos it.
3530 */
3531 protected void setExpandedState(TreePath path, boolean state) {
3532 if(path != null) {
3533 // Make sure all parents of path are expanded.
3534 Stack<TreePath> stack;
3535 TreePath parentPath = path.getParentPath();
3536
3537 if (expandedStack.size() == 0) {
3538 stack = new Stack<TreePath>();
3539 }
3540 else {
3541 stack = expandedStack.pop();
3542 }
3543
3544 try {
3545 while(parentPath != null) {
3546 if(isExpanded(parentPath)) {
3547 parentPath = null;
3548 }
3549 else {
3550 stack.push(parentPath);
3551 parentPath = parentPath.getParentPath();
3552 }
3553 }
3554 for(int counter = stack.size() - 1; counter >= 0; counter--) {
3555 parentPath = stack.pop();
3556 if(!isExpanded(parentPath)) {
3557 try {
3558 fireTreeWillExpand(parentPath);
3559 } catch (ExpandVetoException eve) {
3560 // Expand vetoed!
3561 return;
3562 }
3563 expandedState.put(parentPath, Boolean.TRUE);
3564 fireTreeExpanded(parentPath);
3565 if (accessibleContext != null) {
3566 ((AccessibleJTree)accessibleContext).
3567 fireVisibleDataPropertyChange();
3568 }
3569 }
3570 }
3571 }
3572 finally {
3573 if (expandedStack.size() < TEMP_STACK_SIZE) {
3574 stack.removeAllElements();
3575 expandedStack.push(stack);
3576 }
3577 }
3578 if(!state) {
3579 // collapse last path.
3580 Object cValue = expandedState.get(path);
3581
3582 if(cValue != null && ((Boolean)cValue).booleanValue()) {
3583 try {
3584 fireTreeWillCollapse(path);
3585 }
3586 catch (ExpandVetoException eve) {
3587 return;
3588 }
3589 expandedState.put(path, Boolean.FALSE);
3590 fireTreeCollapsed(path);
3591 if (removeDescendantSelectedPaths(path, false) &&
3592 !isPathSelected(path)) {
3593 // A descendant was selected, select the parent.
3594 addSelectionPath(path);
3595 }
3596 if (accessibleContext != null) {
3597 ((AccessibleJTree)accessibleContext).
3598 fireVisibleDataPropertyChange();
3599 }
3600 }
3601 }
3602 else {
3603 // Expand last path.
3604 Object cValue = expandedState.get(path);
3605
3606 if(cValue == null || !((Boolean)cValue).booleanValue()) {
3607 try {
3608 fireTreeWillExpand(path);
3609 }
3610 catch (ExpandVetoException eve) {
3611 return;
3612 }
3613 expandedState.put(path, Boolean.TRUE);
3614 fireTreeExpanded(path);
3615 if (accessibleContext != null) {
3616 ((AccessibleJTree)accessibleContext).
3617 fireVisibleDataPropertyChange();
3618 }
3619 }
3620 }
3621 }
3622 }
3623
3624 /**
3625 * Returns an <code>Enumeration</code> of <code>TreePaths</code>
3626 * that have been expanded that
3627 * are descendants of <code>parent</code>.
3628 */
3629 protected Enumeration<TreePath>
3630 getDescendantToggledPaths(TreePath parent)
3631 {
3632 if(parent == null)
3633 return null;
3634
3635 Vector<TreePath> descendants = new Vector<TreePath>();
3636 Enumeration<TreePath> nodes = expandedState.keys();
3637
3638 while(nodes.hasMoreElements()) {
3639 TreePath path = nodes.nextElement();
3640 if(parent.isDescendant(path))
3641 descendants.addElement(path);
3642 }
3643 return descendants.elements();
3644 }
3645
3646 /**
3647 * Removes any descendants of the <code>TreePaths</code> in
3648 * <code>toRemove</code>
3649 * that have been expanded.
3650 *
3651 * @param toRemove an enumeration of the paths to remove; a value of
3652 * {@code null} is ignored
3653 * @throws ClassCastException if {@code toRemove} contains an
3654 * element that is not a {@code TreePath}; {@code null}
3655 * values are ignored
3656 */
3657 protected void
3658 removeDescendantToggledPaths(Enumeration<TreePath> toRemove)
3659 {
3660 if(toRemove != null) {
3661 while(toRemove.hasMoreElements()) {
3662 Enumeration descendants = getDescendantToggledPaths
3663 (toRemove.nextElement());
3664
3665 if(descendants != null) {
3666 while(descendants.hasMoreElements()) {
3667 expandedState.remove(descendants.nextElement());
3668 }
3669 }
3670 }
3671 }
3672 }
3673
3674 /**
3675 * Clears the cache of toggled tree paths. This does NOT send out
3676 * any <code>TreeExpansionListener</code> events.
3677 */
3678 protected void clearToggledPaths() {
3679 expandedState.clear();
3680 }
3681
3682 /**
3683 * Creates and returns an instance of <code>TreeModelHandler</code>.
3684 * The returned
3685 * object is responsible for updating the expanded state when the
3686 * <code>TreeModel</code> changes.
3687 * <p>
3688 * For more information on what expanded state means, see the
3689 * <a href=#jtree_description>JTree description</a> above.
3690 */
3691 protected TreeModelListener createTreeModelListener() {
3692 return new TreeModelHandler();
3693 }
3694
3695 /**
3696 * Removes any paths in the selection that are descendants of
3697 * <code>path</code>. If <code>includePath</code> is true and
3698 * <code>path</code> is selected, it will be removed from the selection.
3699 *
3700 * @return true if a descendant was selected
3701 * @since 1.3
3702 */
3703 protected boolean removeDescendantSelectedPaths(TreePath path,
3704 boolean includePath) {
3705 TreePath[] toRemove = getDescendantSelectedPaths(path, includePath);
3706
3707 if (toRemove != null) {
3708 getSelectionModel().removeSelectionPaths(toRemove);
3709 return true;
3710 }
3711 return false;
3712 }
3713
3714 /**
3715 * Returns an array of paths in the selection that are descendants of
3716 * <code>path</code>. The returned array may contain <code>null</code>s.
3717 */
3718 private TreePath[] getDescendantSelectedPaths(TreePath path,
3719 boolean includePath) {
3720 TreeSelectionModel sm = getSelectionModel();
3721 TreePath[] selPaths = (sm != null) ? sm.getSelectionPaths() :
3722 null;
3723
3724 if(selPaths != null) {
3725 boolean shouldRemove = false;
3726
3727 for(int counter = selPaths.length - 1; counter >= 0; counter--) {
3728 if(selPaths[counter] != null &&
3729 path.isDescendant(selPaths[counter]) &&
3730 (!path.equals(selPaths[counter]) || includePath))
3731 shouldRemove = true;
3732 else
3733 selPaths[counter] = null;
3734 }
3735 if(!shouldRemove) {
3736 selPaths = null;
3737 }
3738 return selPaths;
3739 }
3740 return null;
3741 }
3742
3743 /**
3744 * Removes any paths from the selection model that are descendants of
3745 * the nodes identified by in <code>e</code>.
3746 */
3747 void removeDescendantSelectedPaths(TreeModelEvent e) {
3748 TreePath pPath = e.getTreePath();
3749 Object[] oldChildren = e.getChildren();
3750 TreeSelectionModel sm = getSelectionModel();
3751
3752 if (sm != null && pPath != null && oldChildren != null &&
3753 oldChildren.length > 0) {
3754 for (int counter = oldChildren.length - 1; counter >= 0;
3755 counter--) {
3756 // Might be better to call getDescendantSelectedPaths
3757 // numerous times, then push to the model.
3758 removeDescendantSelectedPaths(pPath.pathByAddingChild
3759 (oldChildren[counter]), true);
3760 }
3761 }
3762 }
3763
3764
3765 /**
3766 * Listens to the model and updates the <code>expandedState</code>
3767 * accordingly when nodes are removed, or changed.
3768 */
3769 protected class TreeModelHandler implements TreeModelListener {
3770 public void treeNodesChanged(TreeModelEvent e) { }
3771
3772 public void treeNodesInserted(TreeModelEvent e) { }
3773
3774 public void treeStructureChanged(TreeModelEvent e) {
3775 if(e == null)
3776 return;
3777
3778 // NOTE: If I change this to NOT remove the descendants
3779 // and update BasicTreeUIs treeStructureChanged method
3780 // to update descendants in response to a treeStructureChanged
3781 // event, all the children of the event won't collapse!
3782 TreePath parent = e.getTreePath();
3783
3784 if(parent == null)
3785 return;
3786
3787 if (parent.getPathCount() == 1) {
3788 // New root, remove everything!
3789 clearToggledPaths();
3790 if(treeModel.getRoot() != null &&
3791 !treeModel.isLeaf(treeModel.getRoot())) {
3792 // Mark the root as expanded, if it isn't a leaf.
3793 expandedState.put(parent, Boolean.TRUE);
3794 }
3795 }
3796 else if(expandedState.get(parent) != null) {
3797 Vector<TreePath> toRemove = new Vector<TreePath>(1);
3798 boolean isExpanded = isExpanded(parent);
3799
3800 toRemove.addElement(parent);
3801 removeDescendantToggledPaths(toRemove.elements());
3802 if(isExpanded) {
3803 TreeModel model = getModel();
3804
3805 if(model == null || model.isLeaf
3806 (parent.getLastPathComponent()))
3807 collapsePath(parent);
3808 else
3809 expandedState.put(parent, Boolean.TRUE);
3810 }
3811 }
3812 removeDescendantSelectedPaths(parent, false);
3813 }
3814
3815 public void treeNodesRemoved(TreeModelEvent e) {
3816 if(e == null)
3817 return;
3818
3819 TreePath parent = e.getTreePath();
3820 Object[] children = e.getChildren();
3821
3822 if(children == null)
3823 return;
3824
3825 TreePath rPath;
3826 Vector<TreePath> toRemove
3827 = new Vector<TreePath>(Math.max(1, children.length));
3828
3829 for(int counter = children.length - 1; counter >= 0; counter--) {
3830 rPath = parent.pathByAddingChild(children[counter]);
3831 if(expandedState.get(rPath) != null)
3832 toRemove.addElement(rPath);
3833 }
3834 if(toRemove.size() > 0)
3835 removeDescendantToggledPaths(toRemove.elements());
3836
3837 TreeModel model = getModel();
3838
3839 if(model == null || model.isLeaf(parent.getLastPathComponent()))
3840 expandedState.remove(parent);
3841
3842 removeDescendantSelectedPaths(e);
3843 }
3844 }
3845
3846
3847 /**
3848 * <code>DynamicUtilTreeNode</code> can wrap
3849 * vectors/hashtables/arrays/strings and
3850 * create the appropriate children tree nodes as necessary. It is
3851 * dynamic in that it will only create the children as necessary.
3852 * <p>
3853 * <strong>Warning:</strong>
3854 * Serialized objects of this class will not be compatible with
3855 * future Swing releases. The current serialization support is
3856 * appropriate for short term storage or RMI between applications running
3857 * the same version of Swing. As of 1.4, support for long term storage
3858 * of all JavaBeans<sup><font size="-2">TM</font></sup>
3859 * has been added to the <code>java.beans</code> package.
3860 * Please see {@link java.beans.XMLEncoder}.
3861 */
3862 public static class DynamicUtilTreeNode extends DefaultMutableTreeNode {
3863 /**
3864 * Does the this <code>JTree</code> have children?
3865 * This property is currently not implemented.
3866 */
3867 protected boolean hasChildren;
3868 /** Value to create children with. */
3869 protected Object childValue;
3870 /** Have the children been loaded yet? */
3871 protected boolean loadedChildren;
3872
3873 /**
3874 * Adds to parent all the children in <code>children</code>.
3875 * If <code>children</code> is an array or vector all of its
3876 * elements are added is children, otherwise if <code>children</code>
3877 * is a hashtable all the key/value pairs are added in the order
3878 * <code>Enumeration</code> returns them.
3879 */
3880 public static void createChildren(DefaultMutableTreeNode parent,
3881 Object children) {
3882 if(children instanceof Vector) {
3883 Vector childVector = (Vector)children;
3884
3885 for(int counter = 0, maxCounter = childVector.size();
3886 counter < maxCounter; counter++)
3887 parent.add(new DynamicUtilTreeNode
3888 (childVector.elementAt(counter),
3889 childVector.elementAt(counter)));
3890 }
3891 else if(children instanceof Hashtable) {
3892 Hashtable childHT = (Hashtable)children;
3893 Enumeration keys = childHT.keys();
3894 Object aKey;
3895
3896 while(keys.hasMoreElements()) {
3897 aKey = keys.nextElement();
3898 parent.add(new DynamicUtilTreeNode(aKey,
3899 childHT.get(aKey)));
3900 }
3901 }
3902 else if(children instanceof Object[]) {
3903 Object[] childArray = (Object[])children;
3904
3905 for(int counter = 0, maxCounter = childArray.length;
3906 counter < maxCounter; counter++)
3907 parent.add(new DynamicUtilTreeNode(childArray[counter],
3908 childArray[counter]));
3909 }
3910 }
3911
3912 /**
3913 * Creates a node with the specified object as its value and
3914 * with the specified children. For the node to allow children,
3915 * the children-object must be an array of objects, a
3916 * <code>Vector</code>, or a <code>Hashtable</code> -- even
3917 * if empty. Otherwise, the node is not
3918 * allowed to have children.
3919 *
3920 * @param value the <code>Object</code> that is the value for the
3921 * new node
3922 * @param children an array of <code>Object</code>s, a
3923 * <code>Vector</code>, or a <code>Hashtable</code>
3924 * used to create the child nodes; if any other
3925 * object is specified, or if the value is
3926 * <code>null</code>,
3927 * then the node is not allowed to have children
3928 */
3929 public DynamicUtilTreeNode(Object value, Object children) {
3930 super(value);
3931 loadedChildren = false;
3932 childValue = children;
3933 if(children != null) {
3934 if(children instanceof Vector)
3935 setAllowsChildren(true);
3936 else if(children instanceof Hashtable)
3937 setAllowsChildren(true);
3938 else if(children instanceof Object[])
3939 setAllowsChildren(true);
3940 else
3941 setAllowsChildren(false);
3942 }
3943 else
3944 setAllowsChildren(false);
3945 }
3946
3947 /**
3948 * Returns true if this node allows children. Whether the node
3949 * allows children depends on how it was created.
3950 *
3951 * @return true if this node allows children, false otherwise
3952 * @see #JTree.DynamicUtilTreeNode
3953 */
3954 public boolean isLeaf() {
3955 return !getAllowsChildren();
3956 }
3957
3958 /**
3959 * Returns the number of child nodes.
3960 *
3961 * @return the number of child nodes
3962 */
3963 public int getChildCount() {
3964 if(!loadedChildren)
3965 loadChildren();
3966 return super.getChildCount();
3967 }
3968
3969 /**
3970 * Loads the children based on <code>childValue</code>.
3971 * If <code>childValue</code> is a <code>Vector</code>
3972 * or array each element is added as a child,
3973 * if <code>childValue</code> is a <code>Hashtable</code>
3974 * each key/value pair is added in the order that
3975 * <code>Enumeration</code> returns the keys.
3976 */
3977 protected void loadChildren() {
3978 loadedChildren = true;
3979 createChildren(this, childValue);
3980 }
3981
3982 /**
3983 * Subclassed to load the children, if necessary.
3984 */
3985 public TreeNode getChildAt(int index) {
3986 if(!loadedChildren)
3987 loadChildren();
3988 return super.getChildAt(index);
3989 }
3990
3991 /**
3992 * Subclassed to load the children, if necessary.
3993 */
3994 public Enumeration children() {
3995 if(!loadedChildren)
3996 loadChildren();
3997 return super.children();
3998 }
3999 }
4000
4001 void setUIProperty(String propertyName, Object value) {
4002 if (propertyName == "rowHeight") {
4003 if (!rowHeightSet) {
4004 setRowHeight(((Number)value).intValue());
4005 rowHeightSet = false;
4006 }
4007 } else if (propertyName == "scrollsOnExpand") {
4008 if (!scrollsOnExpandSet) {
4009 setScrollsOnExpand(((Boolean)value).booleanValue());
4010 scrollsOnExpandSet = false;
4011 }
4012 } else if (propertyName == "showsRootHandles") {
4013 if (!showsRootHandlesSet) {
4014 setShowsRootHandles(((Boolean)value).booleanValue());
4015 showsRootHandlesSet = false;
4016 }
4017 } else {
4018 super.setUIProperty(propertyName, value);
4019 }
4020 }
4021
4022
4023 /**
4024 * Returns a string representation of this <code>JTree</code>.
4025 * This method
4026 * is intended to be used only for debugging purposes, and the
4027 * content and format of the returned string may vary between
4028 * implementations. The returned string may be empty but may not
4029 * be <code>null</code>.
4030 *
4031 * @return a string representation of this <code>JTree</code>.
4032 */
4033 protected String paramString() {
4034 String rootVisibleString = (rootVisible ?
4035 "true" : "false");
4036 String showsRootHandlesString = (showsRootHandles ?
4037 "true" : "false");
4038 String editableString = (editable ?
4039 "true" : "false");
4040 String largeModelString = (largeModel ?
4041 "true" : "false");
4042 String invokesStopCellEditingString = (invokesStopCellEditing ?
4043 "true" : "false");
4044 String scrollsOnExpandString = (scrollsOnExpand ?
4045 "true" : "false");
4046
4047 return super.paramString() +
4048 ",editable=" + editableString +
4049 ",invokesStopCellEditing=" + invokesStopCellEditingString +
4050 ",largeModel=" + largeModelString +
4051 ",rootVisible=" + rootVisibleString +
4052 ",rowHeight=" + rowHeight +
4053 ",scrollsOnExpand=" + scrollsOnExpandString +
4054 ",showsRootHandles=" + showsRootHandlesString +
4055 ",toggleClickCount=" + toggleClickCount +
4056 ",visibleRowCount=" + visibleRowCount;
4057 }
4058
4059 /////////////////
4060 // Accessibility support
4061 ////////////////
4062
4063 /**
4064 * Gets the AccessibleContext associated with this JTree.
4065 * For JTrees, the AccessibleContext takes the form of an
4066 * AccessibleJTree.
4067 * A new AccessibleJTree instance is created if necessary.
4068 *
4069 * @return an AccessibleJTree that serves as the
4070 * AccessibleContext of this JTree
4071 */
4072 public AccessibleContext getAccessibleContext() {
4073 if (accessibleContext == null) {
4074 accessibleContext = new AccessibleJTree();
4075 }
4076 return accessibleContext;
4077 }
4078
4079 /**
4080 * This class implements accessibility support for the
4081 * <code>JTree</code> class. It provides an implementation of the
4082 * Java Accessibility API appropriate to tree user-interface elements.
4083 * <p>
4084 * <strong>Warning:</strong>
4085 * Serialized objects of this class will not be compatible with
4086 * future Swing releases. The current serialization support is
4087 * appropriate for short term storage or RMI between applications running
4088 * the same version of Swing. As of 1.4, support for long term storage
4089 * of all JavaBeans<sup><font size="-2">TM</font></sup>
4090 * has been added to the <code>java.beans</code> package.
4091 * Please see {@link java.beans.XMLEncoder}.
4092 */
4093 protected class AccessibleJTree extends AccessibleJComponent
4094 implements AccessibleSelection, TreeSelectionListener,
4095 TreeModelListener, TreeExpansionListener {
4096
4097 TreePath leadSelectionPath;
4098 Accessible leadSelectionAccessible;
4099
4100 public AccessibleJTree() {
4101 // Add a tree model listener for JTree
4102 TreeModel model = JTree.this.getModel();
4103 if (model != null) {
4104 model.addTreeModelListener(this);
4105 }
4106 JTree.this.addTreeExpansionListener(this);
4107 JTree.this.addTreeSelectionListener(this);
4108 leadSelectionPath = JTree.this.getLeadSelectionPath();
4109 leadSelectionAccessible = (leadSelectionPath != null)
4110 ? new AccessibleJTreeNode(JTree.this,
4111 leadSelectionPath,
4112 JTree.this)
4113 : null;
4114 }
4115
4116 /**
4117 * Tree Selection Listener value change method. Used to fire the
4118 * property change
4119 *
4120 * @param e ListSelectionEvent
4121 *
4122 */
4123 public void valueChanged(TreeSelectionEvent e) {
4124 // Fixes 4546503 - JTree is sending incorrect active
4125 // descendant events
4126 TreePath oldLeadSelectionPath = e.getOldLeadSelectionPath();
4127 leadSelectionPath = e.getNewLeadSelectionPath();
4128
4129 if (oldLeadSelectionPath != leadSelectionPath) {
4130 // Set parent to null so AccessibleJTreeNode computes
4131 // its parent.
4132 Accessible oldLSA = leadSelectionAccessible;
4133 leadSelectionAccessible = (leadSelectionPath != null)
4134 ? new AccessibleJTreeNode(JTree.this,
4135 leadSelectionPath,
4136 null) // parent
4137 : null;
4138 firePropertyChange(AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY,
4139 oldLSA, leadSelectionAccessible);
4140 }
4141 firePropertyChange(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
4142 Boolean.valueOf(false), Boolean.valueOf(true));
4143 }
4144
4145 /**
4146 * Fire a visible data property change notification.
4147 * A 'visible' data property is one that represents
4148 * something about the way the component appears on the
4149 * display, where that appearance isn't bound to any other
4150 * property. It notifies screen readers that the visual
4151 * appearance of the component has changed, so they can
4152 * notify the user.
4153 */
4154 public void fireVisibleDataPropertyChange() {
4155 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
4156 Boolean.valueOf(false), Boolean.valueOf(true));
4157 }
4158
4159 // Fire the visible data changes for the model changes.
4160
4161 /**
4162 * Tree Model Node change notification.
4163 *
4164 * @param e a Tree Model event
4165 */
4166 public void treeNodesChanged(TreeModelEvent e) {
4167 fireVisibleDataPropertyChange();
4168 }
4169
4170 /**
4171 * Tree Model Node change notification.
4172 *
4173 * @param e a Tree node insertion event
4174 */
4175 public void treeNodesInserted(TreeModelEvent e) {
4176 fireVisibleDataPropertyChange();
4177 }
4178
4179 /**
4180 * Tree Model Node change notification.
4181 *
4182 * @param e a Tree node(s) removal event
4183 */
4184 public void treeNodesRemoved(TreeModelEvent e) {
4185 fireVisibleDataPropertyChange();
4186 }
4187
4188 /**
4189 * Tree Model structure change change notification.
4190 *
4191 * @param e a Tree Model event
4192 */
4193 public void treeStructureChanged(TreeModelEvent e) {
4194 fireVisibleDataPropertyChange();
4195 }
4196
4197 /**
4198 * Tree Collapsed notification.
4199 *
4200 * @param e a TreeExpansionEvent
4201 */
4202 public void treeCollapsed(TreeExpansionEvent e) {
4203 fireVisibleDataPropertyChange();
4204 TreePath path = e.getPath();
4205 if (path != null) {
4206 // Set parent to null so AccessibleJTreeNode computes
4207 // its parent.
4208 AccessibleJTreeNode node = new AccessibleJTreeNode(JTree.this,
4209 path,
4210 null);
4211 PropertyChangeEvent pce = new PropertyChangeEvent(node,
4212 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
4213 AccessibleState.EXPANDED,
4214 AccessibleState.COLLAPSED);
4215 firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
4216 null, pce);
4217 }
4218 }
4219
4220 /**
4221 * Tree Model Expansion notification.
4222 *
4223 * @param e a Tree node insertion event
4224 */
4225 public void treeExpanded(TreeExpansionEvent e) {
4226 fireVisibleDataPropertyChange();
4227 TreePath path = e.getPath();
4228 if (path != null) {
4229 // TIGER - 4839971
4230 // Set parent to null so AccessibleJTreeNode computes
4231 // its parent.
4232 AccessibleJTreeNode node = new AccessibleJTreeNode(JTree.this,
4233 path,
4234 null);
4235 PropertyChangeEvent pce = new PropertyChangeEvent(node,
4236 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
4237 AccessibleState.COLLAPSED,
4238 AccessibleState.EXPANDED);
4239 firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
4240 null, pce);
4241 }
4242 }
4243
4244
4245 private AccessibleContext getCurrentAccessibleContext() {
4246 Component c = getCurrentComponent();
4247 if (c instanceof Accessible) {
4248 return c.getAccessibleContext();
4249 } else {
4250 return null;
4251 }
4252 }
4253
4254 private Component getCurrentComponent() {
4255 // is the object visible?
4256 // if so, get row, selected, focus & leaf state,
4257 // and then get the renderer component and return it
4258 TreeModel model = JTree.this.getModel();
4259 if (model == null) {
4260 return null;
4261 }
4262 TreePath path = new TreePath(model.getRoot());
4263 if (JTree.this.isVisible(path)) {
4264 TreeCellRenderer r = JTree.this.getCellRenderer();
4265 TreeUI ui = JTree.this.getUI();
4266 if (ui != null) {
4267 int row = ui.getRowForPath(JTree.this, path);
4268 int lsr = JTree.this.getLeadSelectionRow();
4269 boolean hasFocus = JTree.this.isFocusOwner()
4270 && (lsr == row);
4271 boolean selected = JTree.this.isPathSelected(path);
4272 boolean expanded = JTree.this.isExpanded(path);
4273
4274 return r.getTreeCellRendererComponent(JTree.this,
4275 model.getRoot(), selected, expanded,
4276 model.isLeaf(model.getRoot()), row, hasFocus);
4277 }
4278 }
4279 return null;
4280 }
4281
4282 // Overridden methods from AccessibleJComponent
4283
4284 /**
4285 * Get the role of this object.
4286 *
4287 * @return an instance of AccessibleRole describing the role of the
4288 * object
4289 * @see AccessibleRole
4290 */
4291 public AccessibleRole getAccessibleRole() {
4292 return AccessibleRole.TREE;
4293 }
4294
4295 /**
4296 * Returns the <code>Accessible</code> child, if one exists,
4297 * contained at the local coordinate <code>Point</code>.
4298 * Otherwise returns <code>null</code>.
4299 *
4300 * @param p point in local coordinates of this <code>Accessible</code>
4301 * @return the <code>Accessible</code>, if it exists,
4302 * at the specified location; else <code>null</code>
4303 */
4304 public Accessible getAccessibleAt(Point p) {
4305 TreePath path = getClosestPathForLocation(p.x, p.y);
4306 if (path != null) {
4307 // JTree.this is NOT the parent; parent will get computed later
4308 return new AccessibleJTreeNode(JTree.this, path, null);
4309 } else {
4310 return null;
4311 }
4312 }
4313
4314 /**
4315 * Returns the number of top-level children nodes of this
4316 * JTree. Each of these nodes may in turn have children nodes.
4317 *
4318 * @return the number of accessible children nodes in the tree.
4319 */
4320 public int getAccessibleChildrenCount() {
4321 TreeModel model = JTree.this.getModel();
4322 if (model == null) {
4323 return 0;
4324 }
4325 if (isRootVisible()) {
4326 return 1; // the root node
4327 }
4328
4329 // return the root's first set of children count
4330 return model.getChildCount(model.getRoot());
4331 }
4332
4333 /**
4334 * Return the nth Accessible child of the object.
4335 *
4336 * @param i zero-based index of child
4337 * @return the nth Accessible child of the object
4338 */
4339 public Accessible getAccessibleChild(int i) {
4340 TreeModel model = JTree.this.getModel();
4341 if (model == null) {
4342 return null;
4343 }
4344 if (isRootVisible()) {
4345 if (i == 0) { // return the root node Accessible
4346 Object[] objPath = { model.getRoot() };
4347 TreePath path = new TreePath(objPath);
4348 return new AccessibleJTreeNode(JTree.this, path, JTree.this);
4349 } else {
4350 return null;
4351 }
4352 }
4353
4354 // return Accessible for one of root's child nodes
4355 int count = model.getChildCount(model.getRoot());
4356 if (i < 0 || i >= count) {
4357 return null;
4358 }
4359 Object obj = model.getChild(model.getRoot(), i);
4360 Object[] objPath = { model.getRoot(), obj };
4361 TreePath path = new TreePath(objPath);
4362 return new AccessibleJTreeNode(JTree.this, path, JTree.this);
4363 }
4364
4365 /**
4366 * Get the index of this object in its accessible parent.
4367 *
4368 * @return the index of this object in its parent. Since a JTree
4369 * top-level object does not have an accessible parent.
4370 * @see #getAccessibleParent
4371 */
4372 public int getAccessibleIndexInParent() {
4373 // didn't ever need to override this...
4374 return super.getAccessibleIndexInParent();
4375 }
4376
4377 // AccessibleSelection methods
4378 /**
4379 * Get the AccessibleSelection associated with this object. In the
4380 * implementation of the Java Accessibility API for this class,
4381 * return this object, which is responsible for implementing the
4382 * AccessibleSelection interface on behalf of itself.
4383 *
4384 * @return this object
4385 */
4386 public AccessibleSelection getAccessibleSelection() {
4387 return this;
4388 }
4389
4390 /**
4391 * Returns the number of items currently selected.
4392 * If no items are selected, the return value will be 0.
4393 *
4394 * @return the number of items currently selected.
4395 */
4396 public int getAccessibleSelectionCount() {
4397 Object[] rootPath = new Object[1];
4398 rootPath[0] = treeModel.getRoot();
4399 TreePath childPath = new TreePath(rootPath);
4400 if (JTree.this.isPathSelected(childPath)) {
4401 return 1;
4402 } else {
4403 return 0;
4404 }
4405 }
4406
4407 /**
4408 * Returns an Accessible representing the specified selected item
4409 * in the object. If there isn't a selection, or there are
4410 * fewer items selected than the integer passed in, the return
4411 * value will be null.
4412 *
4413 * @param i the zero-based index of selected items
4414 * @return an Accessible containing the selected item
4415 */
4416 public Accessible getAccessibleSelection(int i) {
4417 // The JTree can have only one accessible child, the root.
4418 if (i == 0) {
4419 Object[] rootPath = new Object[1];
4420 rootPath[0] = treeModel.getRoot();
4421 TreePath childPath = new TreePath(rootPath);
4422 if (JTree.this.isPathSelected(childPath)) {
4423 return new AccessibleJTreeNode(JTree.this, childPath, JTree.this);
4424 }
4425 }
4426 return null;
4427 }
4428
4429 /**
4430 * Returns true if the current child of this object is selected.
4431 *
4432 * @param i the zero-based index of the child in this Accessible object.
4433 * @see AccessibleContext#getAccessibleChild
4434 */
4435 public boolean isAccessibleChildSelected(int i) {
4436 // The JTree can have only one accessible child, the root.
4437 if (i == 0) {
4438 Object[] rootPath = new Object[1];
4439 rootPath[0] = treeModel.getRoot();
4440 TreePath childPath = new TreePath(rootPath);
4441 return JTree.this.isPathSelected(childPath);
4442 } else {
4443 return false;
4444 }
4445 }
4446
4447 /**
4448 * Adds the specified selected item in the object to the object's
4449 * selection. If the object supports multiple selections,
4450 * the specified item is added to any existing selection, otherwise
4451 * it replaces any existing selection in the object. If the
4452 * specified item is already selected, this method has no effect.
4453 *
4454 * @param i the zero-based index of selectable items
4455 */
4456 public void addAccessibleSelection(int i) {
4457 TreeModel model = JTree.this.getModel();
4458 if (model != null) {
4459 if (i == 0) {
4460 Object[] objPath = {model.getRoot()};
4461 TreePath path = new TreePath(objPath);
4462 JTree.this.addSelectionPath(path);
4463 }
4464 }
4465 }
4466
4467 /**
4468 * Removes the specified selected item in the object from the object's
4469 * selection. If the specified item isn't currently selected, this
4470 * method has no effect.
4471 *
4472 * @param i the zero-based index of selectable items
4473 */
4474 public void removeAccessibleSelection(int i) {
4475 TreeModel model = JTree.this.getModel();
4476 if (model != null) {
4477 if (i == 0) {
4478 Object[] objPath = {model.getRoot()};
4479 TreePath path = new TreePath(objPath);
4480 JTree.this.removeSelectionPath(path);
4481 }
4482 }
4483 }
4484
4485 /**
4486 * Clears the selection in the object, so that nothing in the
4487 * object is selected.
4488 */
4489 public void clearAccessibleSelection() {
4490 int childCount = getAccessibleChildrenCount();
4491 for (int i = 0; i < childCount; i++) {
4492 removeAccessibleSelection(i);
4493 }
4494 }
4495
4496 /**
4497 * Causes every selected item in the object to be selected
4498 * if the object supports multiple selections.
4499 */
4500 public void selectAllAccessibleSelection() {
4501 TreeModel model = JTree.this.getModel();
4502 if (model != null) {
4503 Object[] objPath = {model.getRoot()};
4504 TreePath path = new TreePath(objPath);
4505 JTree.this.addSelectionPath(path);
4506 }
4507 }
4508
4509 /**
4510 * This class implements accessibility support for the
4511 * <code>JTree</code> child. It provides an implementation of the
4512 * Java Accessibility API appropriate to tree nodes.
4513 */
4514 protected class AccessibleJTreeNode extends AccessibleContext
4515 implements Accessible, AccessibleComponent, AccessibleSelection,
4516 AccessibleAction {
4517
4518 private JTree tree = null;
4519 private TreeModel treeModel = null;
4520 private Object obj = null;
4521 private TreePath path = null;
4522 private Accessible accessibleParent = null;
4523 private int index = 0;
4524 private boolean isLeaf = false;
4525
4526 /**
4527 * Constructs an AccessibleJTreeNode
4528 * @since 1.4
4529 */
4530 public AccessibleJTreeNode(JTree t, TreePath p, Accessible ap) {
4531 tree = t;
4532 path = p;
4533 accessibleParent = ap;
4534 treeModel = t.getModel();
4535 obj = p.getLastPathComponent();
4536 if (treeModel != null) {
4537 isLeaf = treeModel.isLeaf(obj);
4538 }
4539 }
4540
4541 private TreePath getChildTreePath(int i) {
4542 // Tree nodes can't be so complex that they have
4543 // two sets of children -> we're ignoring that case
4544 if (i < 0 || i >= getAccessibleChildrenCount()) {
4545 return null;
4546 } else {
4547 Object childObj = treeModel.getChild(obj, i);
4548 Object[] objPath = path.getPath();
4549 Object[] objChildPath = new Object[objPath.length+1];
4550 java.lang.System.arraycopy(objPath, 0, objChildPath, 0, objPath.length);
4551 objChildPath[objChildPath.length-1] = childObj;
4552 return new TreePath(objChildPath);
4553 }
4554 }
4555
4556 /**
4557 * Get the AccessibleContext associated with this tree node.
4558 * In the implementation of the Java Accessibility API for
4559 * this class, return this object, which is its own
4560 * AccessibleContext.
4561 *
4562 * @return this object
4563 */
4564 public AccessibleContext getAccessibleContext() {
4565 return this;
4566 }
4567
4568 private AccessibleContext getCurrentAccessibleContext() {
4569 Component c = getCurrentComponent();
4570 if (c instanceof Accessible) {
4571 return c.getAccessibleContext();
4572 } else {
4573 return null;
4574 }
4575 }
4576
4577 private Component getCurrentComponent() {
4578 // is the object visible?
4579 // if so, get row, selected, focus & leaf state,
4580 // and then get the renderer component and return it
4581 if (tree.isVisible(path)) {
4582 TreeCellRenderer r = tree.getCellRenderer();
4583 if (r == null) {
4584 return null;
4585 }
4586 TreeUI ui = tree.getUI();
4587 if (ui != null) {
4588 int row = ui.getRowForPath(JTree.this, path);
4589 boolean selected = tree.isPathSelected(path);
4590 boolean expanded = tree.isExpanded(path);
4591 boolean hasFocus = false; // how to tell?? -PK
4592 return r.getTreeCellRendererComponent(tree, obj,
4593 selected, expanded, isLeaf, row, hasFocus);
4594 }
4595 }
4596 return null;
4597 }
4598
4599 // AccessibleContext methods
4600
4601 /**
4602 * Get the accessible name of this object.
4603 *
4604 * @return the localized name of the object; null if this
4605 * object does not have a name
4606 */
4607 public String getAccessibleName() {
4608 AccessibleContext ac = getCurrentAccessibleContext();
4609 if (ac != null) {
4610 String name = ac.getAccessibleName();
4611 if ((name != null) && (name != "")) {
4612 return ac.getAccessibleName();
4613 } else {
4614 return null;
4615 }
4616 }
4617 if ((accessibleName != null) && (accessibleName != "")) {
4618 return accessibleName;
4619 } else {
4620 // fall back to the client property
4621 return (String)getClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY);
4622 }
4623 }
4624
4625 /**
4626 * Set the localized accessible name of this object.
4627 *
4628 * @param s the new localized name of the object.
4629 */
4630 public void setAccessibleName(String s) {
4631 AccessibleContext ac = getCurrentAccessibleContext();
4632 if (ac != null) {
4633 ac.setAccessibleName(s);
4634 } else {
4635 super.setAccessibleName(s);
4636 }
4637 }
4638
4639 //
4640 // *** should check tooltip text for desc. (needs MouseEvent)
4641 //
4642 /**
4643 * Get the accessible description of this object.
4644 *
4645 * @return the localized description of the object; null if
4646 * this object does not have a description
4647 */
4648 public String getAccessibleDescription() {
4649 AccessibleContext ac = getCurrentAccessibleContext();
4650 if (ac != null) {
4651 return ac.getAccessibleDescription();
4652 } else {
4653 return super.getAccessibleDescription();
4654 }
4655 }
4656
4657 /**
4658 * Set the accessible description of this object.
4659 *
4660 * @param s the new localized description of the object
4661 */
4662 public void setAccessibleDescription(String s) {
4663 AccessibleContext ac = getCurrentAccessibleContext();
4664 if (ac != null) {
4665 ac.setAccessibleDescription(s);
4666 } else {
4667 super.setAccessibleDescription(s);
4668 }
4669 }
4670
4671 /**
4672 * Get the role of this object.
4673 *
4674 * @return an instance of AccessibleRole describing the role of the object
4675 * @see AccessibleRole
4676 */
4677 public AccessibleRole getAccessibleRole() {
4678 AccessibleContext ac = getCurrentAccessibleContext();
4679 if (ac != null) {
4680 return ac.getAccessibleRole();
4681 } else {
4682 return AccessibleRole.UNKNOWN;
4683 }
4684 }
4685
4686 /**
4687 * Get the state set of this object.
4688 *
4689 * @return an instance of AccessibleStateSet containing the
4690 * current state set of the object
4691 * @see AccessibleState
4692 */
4693 public AccessibleStateSet getAccessibleStateSet() {
4694 AccessibleContext ac = getCurrentAccessibleContext();
4695 AccessibleStateSet states;
4696 if (ac != null) {
4697 states = ac.getAccessibleStateSet();
4698 } else {
4699 states = new AccessibleStateSet();
4700 }
4701 // need to test here, 'cause the underlying component
4702 // is a cellRenderer, which is never showing...
4703 if (isShowing()) {
4704 states.add(AccessibleState.SHOWING);
4705 } else if (states.contains(AccessibleState.SHOWING)) {
4706 states.remove(AccessibleState.SHOWING);
4707 }
4708 if (isVisible()) {
4709 states.add(AccessibleState.VISIBLE);
4710 } else if (states.contains(AccessibleState.VISIBLE)) {
4711 states.remove(AccessibleState.VISIBLE);
4712 }
4713 if (tree.isPathSelected(path)){
4714 states.add(AccessibleState.SELECTED);
4715 }
4716 if (path == getLeadSelectionPath()) {
4717 states.add(AccessibleState.ACTIVE);
4718 }
4719 if (!isLeaf) {
4720 states.add(AccessibleState.EXPANDABLE);
4721 }
4722 if (tree.isExpanded(path)) {
4723 states.add(AccessibleState.EXPANDED);
4724 } else {
4725 states.add(AccessibleState.COLLAPSED);
4726 }
4727 if (tree.isEditable()) {
4728 states.add(AccessibleState.EDITABLE);
4729 }
4730 return states;
4731 }
4732
4733 /**
4734 * Get the Accessible parent of this object.
4735 *
4736 * @return the Accessible parent of this object; null if this
4737 * object does not have an Accessible parent
4738 */
4739 public Accessible getAccessibleParent() {
4740 // someone wants to know, so we need to create our parent
4741 // if we don't have one (hey, we're a talented kid!)
4742 if (accessibleParent == null) {
4743 Object[] objPath = path.getPath();
4744 if (objPath.length > 1) {
4745 Object objParent = objPath[objPath.length-2];
4746 if (treeModel != null) {
4747 index = treeModel.getIndexOfChild(objParent, obj);
4748 }
4749 Object[] objParentPath = new Object[objPath.length-1];
4750 java.lang.System.arraycopy(objPath, 0, objParentPath,
4751 0, objPath.length-1);
4752 TreePath parentPath = new TreePath(objParentPath);
4753 accessibleParent = new AccessibleJTreeNode(tree,
4754 parentPath,
4755 null);
4756 this.setAccessibleParent(accessibleParent);
4757 } else if (treeModel != null) {
4758 accessibleParent = tree; // we're the top!
4759 index = 0; // we're an only child!
4760 this.setAccessibleParent(accessibleParent);
4761 }
4762 }
4763 return accessibleParent;
4764 }
4765
4766 /**
4767 * Get the index of this object in its accessible parent.
4768 *
4769 * @return the index of this object in its parent; -1 if this
4770 * object does not have an accessible parent.
4771 * @see #getAccessibleParent
4772 */
4773 public int getAccessibleIndexInParent() {
4774 // index is invalid 'till we have an accessibleParent...
4775 if (accessibleParent == null) {
4776 getAccessibleParent();
4777 }
4778 Object[] objPath = path.getPath();
4779 if (objPath.length > 1) {
4780 Object objParent = objPath[objPath.length-2];
4781 if (treeModel != null) {
4782 index = treeModel.getIndexOfChild(objParent, obj);
4783 }
4784 }
4785 return index;
4786 }
4787
4788 /**
4789 * Returns the number of accessible children in the object.
4790 *
4791 * @return the number of accessible children in the object.
4792 */
4793 public int getAccessibleChildrenCount() {
4794 // Tree nodes can't be so complex that they have
4795 // two sets of children -> we're ignoring that case
4796 return treeModel.getChildCount(obj);
4797 }
4798
4799 /**
4800 * Return the specified Accessible child of the object.
4801 *
4802 * @param i zero-based index of child
4803 * @return the Accessible child of the object
4804 */
4805 public Accessible getAccessibleChild(int i) {
4806 // Tree nodes can't be so complex that they have
4807 // two sets of children -> we're ignoring that case
4808 if (i < 0 || i >= getAccessibleChildrenCount()) {
4809 return null;
4810 } else {
4811 Object childObj = treeModel.getChild(obj, i);
4812 Object[] objPath = path.getPath();
4813 Object[] objChildPath = new Object[objPath.length+1];
4814 java.lang.System.arraycopy(objPath, 0, objChildPath, 0, objPath.length);
4815 objChildPath[objChildPath.length-1] = childObj;
4816 TreePath childPath = new TreePath(objChildPath);
4817 return new AccessibleJTreeNode(JTree.this, childPath, this);
4818 }
4819 }
4820
4821 /**
4822 * Gets the locale of the component. If the component does not have
4823 * a locale, then the locale of its parent is returned.
4824 *
4825 * @return This component's locale. If this component does not have
4826 * a locale, the locale of its parent is returned.
4827 * @exception IllegalComponentStateException
4828 * If the Component does not have its own locale and has not yet
4829 * been added to a containment hierarchy such that the locale can be
4830 * determined from the containing parent.
4831 * @see #setLocale
4832 */
4833 public Locale getLocale() {
4834 AccessibleContext ac = getCurrentAccessibleContext();
4835 if (ac != null) {
4836 return ac.getLocale();
4837 } else {
4838 return tree.getLocale();
4839 }
4840 }
4841
4842 /**
4843 * Add a PropertyChangeListener to the listener list.
4844 * The listener is registered for all properties.
4845 *
4846 * @param l The PropertyChangeListener to be added
4847 */
4848 public void addPropertyChangeListener(PropertyChangeListener l) {
4849 AccessibleContext ac = getCurrentAccessibleContext();
4850 if (ac != null) {
4851 ac.addPropertyChangeListener(l);
4852 } else {
4853 super.addPropertyChangeListener(l);
4854 }
4855 }
4856
4857 /**
4858 * Remove a PropertyChangeListener from the listener list.
4859 * This removes a PropertyChangeListener that was registered
4860 * for all properties.
4861 *
4862 * @param l The PropertyChangeListener to be removed
4863 */
4864 public void removePropertyChangeListener(PropertyChangeListener l) {
4865 AccessibleContext ac = getCurrentAccessibleContext();
4866 if (ac != null) {
4867 ac.removePropertyChangeListener(l);
4868 } else {
4869 super.removePropertyChangeListener(l);
4870 }
4871 }
4872
4873 /**
4874 * Get the AccessibleAction associated with this object. In the
4875 * implementation of the Java Accessibility API for this class,
4876 * return this object, which is responsible for implementing the
4877 * AccessibleAction interface on behalf of itself.
4878 *
4879 * @return this object
4880 */
4881 public AccessibleAction getAccessibleAction() {
4882 return this;
4883 }
4884
4885 /**
4886 * Get the AccessibleComponent associated with this object. In the
4887 * implementation of the Java Accessibility API for this class,
4888 * return this object, which is responsible for implementing the
4889 * AccessibleComponent interface on behalf of itself.
4890 *
4891 * @return this object
4892 */
4893 public AccessibleComponent getAccessibleComponent() {
4894 return this; // to override getBounds()
4895 }
4896
4897 /**
4898 * Get the AccessibleSelection associated with this object if one
4899 * exists. Otherwise return null.
4900 *
4901 * @return the AccessibleSelection, or null
4902 */
4903 public AccessibleSelection getAccessibleSelection() {
4904 AccessibleContext ac = getCurrentAccessibleContext();
4905 if (ac != null && isLeaf) {
4906 return getCurrentAccessibleContext().getAccessibleSelection();
4907 } else {
4908 return this;
4909 }
4910 }
4911
4912 /**
4913 * Get the AccessibleText associated with this object if one
4914 * exists. Otherwise return null.
4915 *
4916 * @return the AccessibleText, or null
4917 */
4918 public AccessibleText getAccessibleText() {
4919 AccessibleContext ac = getCurrentAccessibleContext();
4920 if (ac != null) {
4921 return getCurrentAccessibleContext().getAccessibleText();
4922 } else {
4923 return null;
4924 }
4925 }
4926
4927 /**
4928 * Get the AccessibleValue associated with this object if one
4929 * exists. Otherwise return null.
4930 *
4931 * @return the AccessibleValue, or null
4932 */
4933 public AccessibleValue getAccessibleValue() {
4934 AccessibleContext ac = getCurrentAccessibleContext();
4935 if (ac != null) {
4936 return getCurrentAccessibleContext().getAccessibleValue();
4937 } else {
4938 return null;
4939 }
4940 }
4941
4942
4943 // AccessibleComponent methods
4944
4945 /**
4946 * Get the background color of this object.
4947 *
4948 * @return the background color, if supported, of the object;
4949 * otherwise, null
4950 */
4951 public Color getBackground() {
4952 AccessibleContext ac = getCurrentAccessibleContext();
4953 if (ac instanceof AccessibleComponent) {
4954 return ((AccessibleComponent) ac).getBackground();
4955 } else {
4956 Component c = getCurrentComponent();
4957 if (c != null) {
4958 return c.getBackground();
4959 } else {
4960 return null;
4961 }
4962 }
4963 }
4964
4965 /**
4966 * Set the background color of this object.
4967 *
4968 * @param c the new Color for the background
4969 */
4970 public void setBackground(Color c) {
4971 AccessibleContext ac = getCurrentAccessibleContext();
4972 if (ac instanceof AccessibleComponent) {
4973 ((AccessibleComponent) ac).setBackground(c);
4974 } else {
4975 Component cp = getCurrentComponent();
4976 if (cp != null) {
4977 cp.setBackground(c);
4978 }
4979 }
4980 }
4981
4982
4983 /**
4984 * Get the foreground color of this object.
4985 *
4986 * @return the foreground color, if supported, of the object;
4987 * otherwise, null
4988 */
4989 public Color getForeground() {
4990 AccessibleContext ac = getCurrentAccessibleContext();
4991 if (ac instanceof AccessibleComponent) {
4992 return ((AccessibleComponent) ac).getForeground();
4993 } else {
4994 Component c = getCurrentComponent();
4995 if (c != null) {
4996 return c.getForeground();
4997 } else {
4998 return null;
4999 }
5000 }
5001 }
5002
5003 public void setForeground(Color c) {
5004 AccessibleContext ac = getCurrentAccessibleContext();
5005 if (ac instanceof AccessibleComponent) {
5006 ((AccessibleComponent) ac).setForeground(c);
5007 } else {
5008 Component cp = getCurrentComponent();
5009 if (cp != null) {
5010 cp.setForeground(c);
5011 }
5012 }
5013 }
5014
5015 public Cursor getCursor() {
5016 AccessibleContext ac = getCurrentAccessibleContext();
5017 if (ac instanceof AccessibleComponent) {
5018 return ((AccessibleComponent) ac).getCursor();
5019 } else {
5020 Component c = getCurrentComponent();
5021 if (c != null) {
5022 return c.getCursor();
5023 } else {
5024 Accessible ap = getAccessibleParent();
5025 if (ap instanceof AccessibleComponent) {
5026 return ((AccessibleComponent) ap).getCursor();
5027 } else {
5028 return null;
5029 }
5030 }
5031 }
5032 }
5033
5034 public void setCursor(Cursor c) {
5035 AccessibleContext ac = getCurrentAccessibleContext();
5036 if (ac instanceof AccessibleComponent) {
5037 ((AccessibleComponent) ac).setCursor(c);
5038 } else {
5039 Component cp = getCurrentComponent();
5040 if (cp != null) {
5041 cp.setCursor(c);
5042 }
5043 }
5044 }
5045
5046 public Font getFont() {
5047 AccessibleContext ac = getCurrentAccessibleContext();
5048 if (ac instanceof AccessibleComponent) {
5049 return ((AccessibleComponent) ac).getFont();
5050 } else {
5051 Component c = getCurrentComponent();
5052 if (c != null) {
5053 return c.getFont();
5054 } else {
5055 return null;
5056 }
5057 }
5058 }
5059
5060 public void setFont(Font f) {
5061 AccessibleContext ac = getCurrentAccessibleContext();
5062 if (ac instanceof AccessibleComponent) {
5063 ((AccessibleComponent) ac).setFont(f);
5064 } else {
5065 Component c = getCurrentComponent();
5066 if (c != null) {
5067 c.setFont(f);
5068 }
5069 }
5070 }
5071
5072 public FontMetrics getFontMetrics(Font f) {
5073 AccessibleContext ac = getCurrentAccessibleContext();
5074 if (ac instanceof AccessibleComponent) {
5075 return ((AccessibleComponent) ac).getFontMetrics(f);
5076 } else {
5077 Component c = getCurrentComponent();
5078 if (c != null) {
5079 return c.getFontMetrics(f);
5080 } else {
5081 return null;
5082 }
5083 }
5084 }
5085
5086 public boolean isEnabled() {
5087 AccessibleContext ac = getCurrentAccessibleContext();
5088 if (ac instanceof AccessibleComponent) {
5089 return ((AccessibleComponent) ac).isEnabled();
5090 } else {
5091 Component c = getCurrentComponent();
5092 if (c != null) {
5093 return c.isEnabled();
5094 } else {
5095 return false;
5096 }
5097 }
5098 }
5099
5100 public void setEnabled(boolean b) {
5101 AccessibleContext ac = getCurrentAccessibleContext();
5102 if (ac instanceof AccessibleComponent) {
5103 ((AccessibleComponent) ac).setEnabled(b);
5104 } else {
5105 Component c = getCurrentComponent();
5106 if (c != null) {
5107 c.setEnabled(b);
5108 }
5109 }
5110 }
5111
5112 public boolean isVisible() {
5113 Rectangle pathBounds = tree.getPathBounds(path);
5114 Rectangle parentBounds = tree.getVisibleRect();
5115 return pathBounds != null && parentBounds != null &&
5116 parentBounds.intersects(pathBounds);
5117 }
5118
5119 public void setVisible(boolean b) {
5120 }
5121
5122 public boolean isShowing() {
5123 return (tree.isShowing() && isVisible());
5124 }
5125
5126 public boolean contains(Point p) {
5127 AccessibleContext ac = getCurrentAccessibleContext();
5128 if (ac instanceof AccessibleComponent) {
5129 Rectangle r = ((AccessibleComponent) ac).getBounds();
5130 return r.contains(p);
5131 } else {
5132 Component c = getCurrentComponent();
5133 if (c != null) {
5134 Rectangle r = c.getBounds();
5135 return r.contains(p);
5136 } else {
5137 return getBounds().contains(p);
5138 }
5139 }
5140 }
5141
5142 public Point getLocationOnScreen() {
5143 if (tree != null) {
5144 Point treeLocation = tree.getLocationOnScreen();
5145 Rectangle pathBounds = tree.getPathBounds(path);
5146 if (treeLocation != null && pathBounds != null) {
5147 Point nodeLocation = new Point(pathBounds.x,
5148 pathBounds.y);
5149 nodeLocation.translate(treeLocation.x, treeLocation.y);
5150 return nodeLocation;
5151 } else {
5152 return null;
5153 }
5154 } else {
5155 return null;
5156 }
5157 }
5158
5159 protected Point getLocationInJTree() {
5160 Rectangle r = tree.getPathBounds(path);
5161 if (r != null) {
5162 return r.getLocation();
5163 } else {
5164 return null;
5165 }
5166 }
5167
5168 public Point getLocation() {
5169 Rectangle r = getBounds();
5170 if (r != null) {
5171 return r.getLocation();
5172 } else {
5173 return null;
5174 }
5175 }
5176
5177 public void setLocation(Point p) {
5178 }
5179
5180 public Rectangle getBounds() {
5181 Rectangle r = tree.getPathBounds(path);
5182 Accessible parent = getAccessibleParent();
5183 if (parent != null) {
5184 if (parent instanceof AccessibleJTreeNode) {
5185 Point parentLoc = ((AccessibleJTreeNode) parent).getLocationInJTree();
5186 if (parentLoc != null && r != null) {
5187 r.translate(-parentLoc.x, -parentLoc.y);
5188 } else {
5189 return null; // not visible!
5190 }
5191 }
5192 }
5193 return r;
5194 }
5195
5196 public void setBounds(Rectangle r) {
5197 AccessibleContext ac = getCurrentAccessibleContext();
5198 if (ac instanceof AccessibleComponent) {
5199 ((AccessibleComponent) ac).setBounds(r);
5200 } else {
5201 Component c = getCurrentComponent();
5202 if (c != null) {
5203 c.setBounds(r);
5204 }
5205 }
5206 }
5207
5208 public Dimension getSize() {
5209 return getBounds().getSize();
5210 }
5211
5212 public void setSize (Dimension d) {
5213 AccessibleContext ac = getCurrentAccessibleContext();
5214 if (ac instanceof AccessibleComponent) {
5215 ((AccessibleComponent) ac).setSize(d);
5216 } else {
5217 Component c = getCurrentComponent();
5218 if (c != null) {
5219 c.setSize(d);
5220 }
5221 }
5222 }
5223
5224 /**
5225 * Returns the <code>Accessible</code> child, if one exists,
5226 * contained at the local coordinate <code>Point</code>.
5227 * Otherwise returns <code>null</code>.
5228 *
5229 * @param p point in local coordinates of this
5230 * <code>Accessible</code>
5231 * @return the <code>Accessible</code>, if it exists,
5232 * at the specified location; else <code>null</code>
5233 */
5234 public Accessible getAccessibleAt(Point p) {
5235 AccessibleContext ac = getCurrentAccessibleContext();
5236 if (ac instanceof AccessibleComponent) {
5237 return ((AccessibleComponent) ac).getAccessibleAt(p);
5238 } else {
5239 return null;
5240 }
5241 }
5242
5243 public boolean isFocusTraversable() {
5244 AccessibleContext ac = getCurrentAccessibleContext();
5245 if (ac instanceof AccessibleComponent) {
5246 return ((AccessibleComponent) ac).isFocusTraversable();
5247 } else {
5248 Component c = getCurrentComponent();
5249 if (c != null) {
5250 return c.isFocusTraversable();
5251 } else {
5252 return false;
5253 }
5254 }
5255 }
5256
5257 public void requestFocus() {
5258 AccessibleContext ac = getCurrentAccessibleContext();
5259 if (ac instanceof AccessibleComponent) {
5260 ((AccessibleComponent) ac).requestFocus();
5261 } else {
5262 Component c = getCurrentComponent();
5263 if (c != null) {
5264 c.requestFocus();
5265 }
5266 }
5267 }
5268
5269 public void addFocusListener(FocusListener l) {
5270 AccessibleContext ac = getCurrentAccessibleContext();
5271 if (ac instanceof AccessibleComponent) {
5272 ((AccessibleComponent) ac).addFocusListener(l);
5273 } else {
5274 Component c = getCurrentComponent();
5275 if (c != null) {
5276 c.addFocusListener(l);
5277 }
5278 }
5279 }
5280
5281 public void removeFocusListener(FocusListener l) {
5282 AccessibleContext ac = getCurrentAccessibleContext();
5283 if (ac instanceof AccessibleComponent) {
5284 ((AccessibleComponent) ac).removeFocusListener(l);
5285 } else {
5286 Component c = getCurrentComponent();
5287 if (c != null) {
5288 c.removeFocusListener(l);
5289 }
5290 }
5291 }
5292
5293 // AccessibleSelection methods
5294
5295 /**
5296 * Returns the number of items currently selected.
5297 * If no items are selected, the return value will be 0.
5298 *
5299 * @return the number of items currently selected.
5300 */
5301 public int getAccessibleSelectionCount() {
5302 int count = 0;
5303 int childCount = getAccessibleChildrenCount();
5304 for (int i = 0; i < childCount; i++) {
5305 TreePath childPath = getChildTreePath(i);
5306 if (tree.isPathSelected(childPath)) {
5307 count++;
5308 }
5309 }
5310 return count;
5311 }
5312
5313 /**
5314 * Returns an Accessible representing the specified selected item
5315 * in the object. If there isn't a selection, or there are
5316 * fewer items selected than the integer passed in, the return
5317 * value will be null.
5318 *
5319 * @param i the zero-based index of selected items
5320 * @return an Accessible containing the selected item
5321 */
5322 public Accessible getAccessibleSelection(int i) {
5323 int childCount = getAccessibleChildrenCount();
5324 if (i < 0 || i >= childCount) {
5325 return null; // out of range
5326 }
5327 int count = 0;
5328 for (int j = 0; j < childCount && i >= count; j++) {
5329 TreePath childPath = getChildTreePath(j);
5330 if (tree.isPathSelected(childPath)) {
5331 if (count == i) {
5332 return new AccessibleJTreeNode(tree, childPath, this);
5333 } else {
5334 count++;
5335 }
5336 }
5337 }
5338 return null;
5339 }
5340
5341 /**
5342 * Returns true if the current child of this object is selected.
5343 *
5344 * @param i the zero-based index of the child in this Accessible
5345 * object.
5346 * @see AccessibleContext#getAccessibleChild
5347 */
5348 public boolean isAccessibleChildSelected(int i) {
5349 int childCount = getAccessibleChildrenCount();
5350 if (i < 0 || i >= childCount) {
5351 return false; // out of range
5352 } else {
5353 TreePath childPath = getChildTreePath(i);
5354 return tree.isPathSelected(childPath);
5355 }
5356 }
5357
5358 /**
5359 * Adds the specified selected item in the object to the object's
5360 * selection. If the object supports multiple selections,
5361 * the specified item is added to any existing selection, otherwise
5362 * it replaces any existing selection in the object. If the
5363 * specified item is already selected, this method has no effect.
5364 *
5365 * @param i the zero-based index of selectable items
5366 */
5367 public void addAccessibleSelection(int i) {
5368 TreeModel model = JTree.this.getModel();
5369 if (model != null) {
5370 if (i >= 0 && i < getAccessibleChildrenCount()) {
5371 TreePath path = getChildTreePath(i);
5372 JTree.this.addSelectionPath(path);
5373 }
5374 }
5375 }
5376
5377 /**
5378 * Removes the specified selected item in the object from the
5379 * object's
5380 * selection. If the specified item isn't currently selected, this
5381 * method has no effect.
5382 *
5383 * @param i the zero-based index of selectable items
5384 */
5385 public void removeAccessibleSelection(int i) {
5386 TreeModel model = JTree.this.getModel();
5387 if (model != null) {
5388 if (i >= 0 && i < getAccessibleChildrenCount()) {
5389 TreePath path = getChildTreePath(i);
5390 JTree.this.removeSelectionPath(path);
5391 }
5392 }
5393 }
5394
5395 /**
5396 * Clears the selection in the object, so that nothing in the
5397 * object is selected.
5398 */
5399 public void clearAccessibleSelection() {
5400 int childCount = getAccessibleChildrenCount();
5401 for (int i = 0; i < childCount; i++) {
5402 removeAccessibleSelection(i);
5403 }
5404 }
5405
5406 /**
5407 * Causes every selected item in the object to be selected
5408 * if the object supports multiple selections.
5409 */
5410 public void selectAllAccessibleSelection() {
5411 TreeModel model = JTree.this.getModel();
5412 if (model != null) {
5413 int childCount = getAccessibleChildrenCount();
5414 TreePath path;
5415 for (int i = 0; i < childCount; i++) {
5416 path = getChildTreePath(i);
5417 JTree.this.addSelectionPath(path);
5418 }
5419 }
5420 }
5421
5422 // AccessibleAction methods
5423
5424 /**
5425 * Returns the number of accessible actions available in this
5426 * tree node. If this node is not a leaf, there is at least
5427 * one action (toggle expand), in addition to any available
5428 * on the object behind the TreeCellRenderer.
5429 *
5430 * @return the number of Actions in this object
5431 */
5432 public int getAccessibleActionCount() {
5433 AccessibleContext ac = getCurrentAccessibleContext();
5434 if (ac != null) {
5435 AccessibleAction aa = ac.getAccessibleAction();
5436 if (aa != null) {
5437 return (aa.getAccessibleActionCount() + (isLeaf ? 0 : 1));
5438 }
5439 }
5440 return isLeaf ? 0 : 1;
5441 }
5442
5443 /**
5444 * Return a description of the specified action of the tree node.
5445 * If this node is not a leaf, there is at least one action
5446 * description (toggle expand), in addition to any available
5447 * on the object behind the TreeCellRenderer.
5448 *
5449 * @param i zero-based index of the actions
5450 * @return a description of the action
5451 */
5452 public String getAccessibleActionDescription(int i) {
5453 if (i < 0 || i >= getAccessibleActionCount()) {
5454 return null;
5455 }
5456 AccessibleContext ac = getCurrentAccessibleContext();
5457 if (i == 0) {
5458 // TIGER - 4766636
5459 return AccessibleAction.TOGGLE_EXPAND;
5460 } else if (ac != null) {
5461 AccessibleAction aa = ac.getAccessibleAction();
5462 if (aa != null) {
5463 return aa.getAccessibleActionDescription(i - 1);
5464 }
5465 }
5466 return null;
5467 }
5468
5469 /**
5470 * Perform the specified Action on the tree node. If this node
5471 * is not a leaf, there is at least one action which can be
5472 * done (toggle expand), in addition to any available on the
5473 * object behind the TreeCellRenderer.
5474 *
5475 * @param i zero-based index of actions
5476 * @return true if the the action was performed; else false.
5477 */
5478 public boolean doAccessibleAction(int i) {
5479 if (i < 0 || i >= getAccessibleActionCount()) {
5480 return false;
5481 }
5482 AccessibleContext ac = getCurrentAccessibleContext();
5483 if (i == 0) {
5484 if (JTree.this.isExpanded(path)) {
5485 JTree.this.collapsePath(path);
5486 } else {
5487 JTree.this.expandPath(path);
5488 }
5489 return true;
5490 } else if (ac != null) {
5491 AccessibleAction aa = ac.getAccessibleAction();
5492 if (aa != null) {
5493 return aa.doAccessibleAction(i - 1);
5494 }
5495 }
5496 return false;
5497 }
5498
5499 } // inner class AccessibleJTreeNode
5500
5501 } // inner class AccessibleJTree
5502
5503 } // End of class JTree